From fe1f6421d2b647adb706e65f69ec2e40e604fac3 Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Fri, 24 Sep 2021 15:24:11 -0700 Subject: [PATCH] feat(core): add `getNgModuleById` function to retrieve loaded NgModules by id (#43580) This commit adds a new function called `getNgModuleById` to the public API surface of the framework. The `getNgModuleById` function allows to retrieve loaded NgModule class by id (specified via `@NgModule.id`). The function is a replacement for the `getModuleFactory` function. DEPRECATION: The `getModuleFactory` function is deprecated in favor of the `getNgModuleById` one. With Ivy it's possible to work with NgModule classes directly, without retrieving corresponding factories, so the `getNgModuleById` should be used instead. PR Close #43580 --- aio/content/guide/deprecations.md | 3 +++ goldens/public-api/core/core.md | 5 +++- .../core/src/core_render3_private_export.ts | 1 + packages/core/src/linker.ts | 2 +- .../src/linker/ng_module_factory_loader.ts | 27 ++++++++++++++++--- .../linker/ng_module_factory_registration.ts | 5 ++-- packages/core/src/metadata/ng_module.ts | 5 ++-- .../test/linker/ng_module_integration_spec.ts | 20 +++++++++++++- 8 files changed, 57 insertions(+), 11 deletions(-) diff --git a/aio/content/guide/deprecations.md b/aio/content/guide/deprecations.md index d6aadfdf03e..ce6e65ea2c6 100644 --- a/aio/content/guide/deprecations.md +++ b/aio/content/guide/deprecations.md @@ -30,6 +30,7 @@ v9 - v12 v10 - v13 v11 - v14 v12 - v15 +v13 -> v16 --> | Area | API or Feature | May be removed in | @@ -42,6 +43,7 @@ v12 - v15 | `@angular/core` | [`RenderComponentType`](#core) | v11 | | `@angular/core` | [Factory-based signature of `ApplicationRef.bootstrap`](#core) | v15 | | `@angular/core` | [`PlatformRef.bootstrapModuleFactory`](#core) | v15 | +| `@angular/core` | [`getModuleFactory`](#core) | v16 | | `@angular/forms` | [`ngModel` with reactive forms](#ngmodel-reactive) | v11 | | `@angular/upgrade` | [`@angular/upgrade`](#upgrade) | v11 | | `@angular/upgrade` | [`getAngularLib`](#upgrade-static) | v11 | @@ -105,6 +107,7 @@ This section contains a complete list all of the currently-deprecated APIs, with | [`entryComponents`](api/core/NgModule#entryComponents) | none | v9 | See [`entryComponents`](#entryComponents) | | [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | none | v9 | See [`ANALYZE_FOR_ENTRY_COMPONENTS`](#entryComponents) | | [`async`](api/core/testing/async) | [`waitForAsync`](api/core/testing/waitForAsync) | v11 | The [`async`](api/core/testing/async) function from `@angular/core/testing` has been renamed to `waitForAsync` in order to avoid confusion with the native JavaScript async syntax. The existing function is deprecated and will be removed in a future version. | +| [`getModuleFactory`](api/core/getModuleFactory) | [`getNgModuleById`](api/core/getNgModuleById) | v13 | Ivy allows working with NgModule classes directly, without retrieving corresponding factories. | | `ViewChildren.emitDistinctChangesOnly` / `ContentChildren.emitDistinctChangesOnly` | none (was part of [issue #40091](https://github.com/angular/angular/issues/40091)) | | This is a temporary flag introduced as part of bugfix of [issue #40091](https://github.com/angular/angular/issues/40091) and will be removed. | | Factory-based signature of [`ApplicationRef.bootstrap`](api/core/ApplicationRef#bootstrap) | Type-based signature of [`ApplicationRef.bootstrap`](api/core/ApplicationRef#bootstrap) | v13 | With Ivy, there is no need to resolve Component factory and Component Type can be provided directly. | | [`PlatformRef.bootstrapModuleFactory`](api/core/PlatformRef#bootstrapModuleFactory) | [`PlatformRef.bootstrapModule`](api/core/PlatformRef#bootstrapModule) | v13 | With Ivy, there is no need to resolve NgModule factory and NgModule Type can be provided directly. | diff --git a/goldens/public-api/core/core.md b/goldens/public-api/core/core.md index 1c20508fc8e..7c498a4abe7 100644 --- a/goldens/public-api/core/core.md +++ b/goldens/public-api/core/core.md @@ -496,9 +496,12 @@ export interface ForwardRefFn { // @public (undocumented) export const getDebugNode: (nativeNode: any) => DebugNode | null; -// @public +// @public @deprecated export const getModuleFactory: (id: string) => NgModuleFactory; +// @public +export const getNgModuleById: (id: string) => Type; + // @public export function getPlatform(): PlatformRef | null; diff --git a/packages/core/src/core_render3_private_export.ts b/packages/core/src/core_render3_private_export.ts index 4283ecdb179..cbdad67b2bf 100644 --- a/packages/core/src/core_render3_private_export.ts +++ b/packages/core/src/core_render3_private_export.ts @@ -44,6 +44,7 @@ export { SWITCH_ELEMENT_REF_FACTORY__POST_R3__ as ɵSWITCH_ELEMENT_REF_FACTORY__POST_R3__, } from './linker/element_ref'; export { getModuleFactory__POST_R3__ as ɵgetModuleFactory__POST_R3__ } from './linker/ng_module_factory_loader'; +export { getNgModuleById__POST_R3__ as ɵgetNgModuleById__POST_R3__ } from './linker/ng_module_factory_loader'; export { registerNgModuleType as ɵregisterNgModuleType } from './linker/ng_module_factory_registration'; export { SWITCH_TEMPLATE_REF_FACTORY__POST_R3__ as ɵSWITCH_TEMPLATE_REF_FACTORY__POST_R3__, diff --git a/packages/core/src/linker.ts b/packages/core/src/linker.ts index d923e7af91b..8b0cd8a861a 100644 --- a/packages/core/src/linker.ts +++ b/packages/core/src/linker.ts @@ -12,7 +12,7 @@ export {ComponentFactory, ComponentRef} from './linker/component_factory'; export {ComponentFactoryResolver} from './linker/component_factory_resolver'; export {ElementRef} from './linker/element_ref'; export {NgModuleFactory, NgModuleRef} from './linker/ng_module_factory'; -export {getModuleFactory} from './linker/ng_module_factory_loader'; +export {getModuleFactory, getNgModuleById} from './linker/ng_module_factory_loader'; export {QueryList} from './linker/query_list'; export {TemplateRef} from './linker/template_ref'; export {ViewContainerRef} from './linker/view_container_ref'; diff --git a/packages/core/src/linker/ng_module_factory_loader.ts b/packages/core/src/linker/ng_module_factory_loader.ts index 44a5a3ec404..ed48139747b 100644 --- a/packages/core/src/linker/ng_module_factory_loader.ts +++ b/packages/core/src/linker/ng_module_factory_loader.ts @@ -6,6 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ +import {Type} from '../interface/type'; + import {NgModuleType} from '../metadata/ng_module_def'; import {NgModuleFactory as R3NgModuleFactory} from '../render3/ng_module_ref'; @@ -24,14 +26,33 @@ export function getModuleFactory__POST_R3__(id: string): NgModuleFactory { return new R3NgModuleFactory(type); } +export function getNgModuleById__PRE_R3__(id: string): NgModuleType { + throw new Error(`ViewEngine doesn't support retrieving NgModule classes by id`); +} + +export function getNgModuleById__POST_R3__(id: string): NgModuleType { + const type = getRegisteredNgModuleType(id); + if (!type) throw noModuleError(id); + return type; +} + /** - * Returns the NgModuleFactory with the given id, if it exists and has been loaded. - * Factories for modules that do not specify an `id` cannot be retrieved. Throws if the module - * cannot be found. + * Returns the NgModuleFactory with the given id (specified using [@NgModule.id + * field](api/core/NgModule#id)), if it exists and has been loaded. Factories for NgModules that do + * not specify an `id` cannot be retrieved. Throws if an NgModule cannot be found. * @publicApi + * @deprecated Use `getNgModuleById` instead. */ export const getModuleFactory: (id: string) => NgModuleFactory = getModuleFactory__PRE_R3__; +/** + * Returns the NgModule class with the given id (specified using [@NgModule.id + * field](api/core/NgModule#id)), if it exists and has been loaded. Classes for NgModules that do + * not specify an `id` cannot be retrieved. Throws if an NgModule cannot be found. + * @publicApi + */ +export const getNgModuleById: (id: string) => Type = getNgModuleById__PRE_R3__; + function noModuleError( id: string, ): Error { diff --git a/packages/core/src/linker/ng_module_factory_registration.ts b/packages/core/src/linker/ng_module_factory_registration.ts index 2462d86b1bc..dcfe5a38e97 100644 --- a/packages/core/src/linker/ng_module_factory_registration.ts +++ b/packages/core/src/linker/ng_module_factory_registration.ts @@ -68,6 +68,7 @@ export function clearModulesForTest(): void { modules.clear(); } -export function getRegisteredNgModuleType(id: string) { - return modules.get(id) || autoRegisterModuleById[id]; +export function getRegisteredNgModuleType|NgModuleType>( + id: string): T|undefined { + return (modules.get(id) || autoRegisterModuleById[id]) as T | undefined; } diff --git a/packages/core/src/metadata/ng_module.ts b/packages/core/src/metadata/ng_module.ts index acbd2000d34..43b4a1c0fff 100644 --- a/packages/core/src/metadata/ng_module.ts +++ b/packages/core/src/metadata/ng_module.ts @@ -226,9 +226,8 @@ export interface NgModule { schemas?: Array; /** - * A name or path that uniquely identifies this NgModule in `getModuleFactory`. - * If left `undefined`, the NgModule is not registered with - * `getModuleFactory`. + * A name or path that uniquely identifies this NgModule in `getNgModuleById`. + * If left `undefined`, the NgModule is not registered with `getNgModuleById`. */ id?: string; diff --git a/packages/core/test/linker/ng_module_integration_spec.ts b/packages/core/test/linker/ng_module_integration_spec.ts index e5f2b36329b..0a736f60aca 100644 --- a/packages/core/test/linker/ng_module_integration_spec.ts +++ b/packages/core/test/linker/ng_module_integration_spec.ts @@ -6,8 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {ANALYZE_FOR_ENTRY_COMPONENTS, Compiler, Component, ComponentFactoryResolver, CUSTOM_ELEMENTS_SCHEMA, Directive, forwardRef, getModuleFactory, HostBinding, Inject, Injectable, InjectionToken, Injector, Input, NgModule, NgModuleRef, Optional, Pipe, Provider, Self, Type, ɵivyEnabled as ivyEnabled, ɵɵdefineNgModule as defineNgModule} from '@angular/core'; +import {ANALYZE_FOR_ENTRY_COMPONENTS, Compiler, Component, ComponentFactoryResolver, CUSTOM_ELEMENTS_SCHEMA, Directive, forwardRef, getModuleFactory, getNgModuleById, HostBinding, Inject, Injectable, InjectionToken, Injector, Input, NgModule, NgModuleRef, Optional, Pipe, Provider, Self, Type, ɵivyEnabled as ivyEnabled, ɵɵdefineNgModule as defineNgModule} from '@angular/core'; import {ɵɵdefineInjectable} from '@angular/core/src/di/interface/defs'; +import {NgModuleType} from '@angular/core/src/render3'; import {getNgModuleDef} from '@angular/core/src/render3/definition'; import {NgModuleData} from '@angular/core/src/view/types'; import {tokenKey} from '@angular/core/src/view/util'; @@ -329,6 +330,17 @@ function declareTests(config?: {useJit: boolean}) { class SomeModule { } createModule(SomeModule); + + if (ivyEnabled) { + const moduleType = getNgModuleById(token); + expect(moduleType).toBeTruthy(); + expect(moduleType).toBe(SomeModule as NgModuleType); + } else { + // ViewEngine doesn't support this call, expect it to throw. + expect(() => getNgModuleById(token)) + .toThrowError(`ViewEngine doesn't support retrieving NgModule classes by id`); + } + const factory = getModuleFactory(token); expect(factory).toBeTruthy(); expect(factory.moduleType).toBe(SomeModule); @@ -377,8 +389,14 @@ function declareTests(config?: {useJit: boolean}) { }); } + // Verify that we can retrieve NgModule factory by id. createModuleFactory(ChildModule); expect(getModuleFactory('child')).toBeAnInstanceOf(NgModuleFactory); + + // Verify that we can also retrieve NgModule class by id. + const moduleType = getNgModuleById('child'); + expect(moduleType).toBeTruthy(); + expect(moduleType).toBe(ChildModule as NgModuleType); }); });