docs(docs-infra): compile module first for Ivy when loading element modules (#30704)
In View Engine, NgModule factories are created for each NgModule and loaded when the module is requested. Ivy doesn't generate the factories by design and only loads the module class, so it must be compiled after being loaded. PR Close #30704
This commit is contained in:
parent
fcef39048a
commit
32886cf9ac
|
@ -1,4 +1,4 @@
|
|||
import { NgModule, NgModuleFactoryLoader, SystemJsNgModuleLoader } from '@angular/core';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { ROUTES} from '@angular/router';
|
||||
import { ElementsLoader } from './elements-loader';
|
||||
import {
|
||||
|
@ -13,7 +13,6 @@ import { LazyCustomElementComponent } from './lazy-custom-element.component';
|
|||
exports: [ LazyCustomElementComponent ],
|
||||
providers: [
|
||||
ElementsLoader,
|
||||
{ provide: NgModuleFactoryLoader, useClass: SystemJsNgModuleLoader },
|
||||
{ provide: ELEMENT_MODULE_LOAD_CALLBACKS_TOKEN, useValue: ELEMENT_MODULE_LOAD_CALLBACKS },
|
||||
|
||||
// Providing these routes as a signal to the build system that these modules should be
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import {
|
||||
Compiler,
|
||||
ComponentFactory,
|
||||
ComponentFactoryResolver, ComponentRef, Injector, NgModuleFactory,
|
||||
NgModuleRef,
|
||||
Type
|
||||
Type,
|
||||
} from '@angular/core';
|
||||
import { TestBed, fakeAsync, flushMicrotasks } from '@angular/core/testing';
|
||||
|
||||
|
@ -17,19 +18,25 @@ interface Deferred {
|
|||
|
||||
describe('ElementsLoader', () => {
|
||||
let elementsLoader: ElementsLoader;
|
||||
let compiler: Compiler;
|
||||
|
||||
beforeEach(() => {
|
||||
const injector = TestBed.configureTestingModule({
|
||||
providers: [
|
||||
ElementsLoader,
|
||||
{ provide: ELEMENT_MODULE_LOAD_CALLBACKS_TOKEN, useValue: new Map<string, () => Promise<NgModuleFactory<WithCustomElementComponent>>>([
|
||||
{
|
||||
provide: ELEMENT_MODULE_LOAD_CALLBACKS_TOKEN, useValue: new Map<
|
||||
string, () => Promise<NgModuleFactory<WithCustomElementComponent> | Type<WithCustomElementComponent>>
|
||||
>([
|
||||
['element-a-selector', () => Promise.resolve(new FakeModuleFactory('element-a-module'))],
|
||||
['element-b-selector', () => Promise.resolve(new FakeModuleFactory('element-b-module'))]
|
||||
['element-b-selector', () => Promise.resolve(new FakeModuleFactory('element-b-module'))],
|
||||
['element-c-selector', () => Promise.resolve(FakeCustomElementModule)]
|
||||
])},
|
||||
]
|
||||
});
|
||||
|
||||
elementsLoader = injector.get(ElementsLoader);
|
||||
compiler = injector.get(Compiler);
|
||||
});
|
||||
|
||||
describe('loadContainedCustomElements()', () => {
|
||||
|
@ -221,6 +228,20 @@ describe('ElementsLoader', () => {
|
|||
expect(definedSpy).toHaveBeenCalledTimes(1);
|
||||
})
|
||||
);
|
||||
|
||||
it('should be able to load and register an element after compiling its NgModule', fakeAsync(() => {
|
||||
const compilerSpy = spyOn(compiler, 'compileModuleAsync')
|
||||
.and.returnValue(Promise.resolve(new FakeModuleFactory('element-c-module')));
|
||||
|
||||
elementsLoader.loadCustomElement('element-c-selector');
|
||||
flushMicrotasks();
|
||||
|
||||
expect(definedSpy).toHaveBeenCalledTimes(1);
|
||||
expect(definedSpy).toHaveBeenCalledWith('element-c-selector', jasmine.any(Function));
|
||||
|
||||
expect(compilerSpy).toHaveBeenCalledTimes(1);
|
||||
expect(compilerSpy).toHaveBeenCalledWith(FakeCustomElementModule);
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import {
|
||||
Compiler,
|
||||
Inject,
|
||||
Injectable,
|
||||
NgModuleFactory,
|
||||
NgModuleRef,
|
||||
Type,
|
||||
} from '@angular/core';
|
||||
import { ELEMENT_MODULE_LOAD_CALLBACKS_TOKEN, WithCustomElementComponent } from './element-registry';
|
||||
import { from, Observable, of } from 'rxjs';
|
||||
|
@ -18,7 +20,8 @@ export class ElementsLoader {
|
|||
private elementsLoading = new Map<string, Promise<void>>();
|
||||
|
||||
constructor(private moduleRef: NgModuleRef<any>,
|
||||
@Inject(ELEMENT_MODULE_LOAD_CALLBACKS_TOKEN) elementModulePaths: Map<string, LoadChildrenCallback>) {
|
||||
@Inject(ELEMENT_MODULE_LOAD_CALLBACKS_TOKEN) elementModulePaths: Map<string, LoadChildrenCallback>,
|
||||
private compiler: Compiler) {
|
||||
this.elementsToLoad = new Map(elementModulePaths);
|
||||
}
|
||||
|
||||
|
@ -48,7 +51,22 @@ export class ElementsLoader {
|
|||
if (this.elementsToLoad.has(selector)) {
|
||||
// Load and register the custom element (for the first time).
|
||||
const modulePathLoader = this.elementsToLoad.get(selector)!;
|
||||
const loadedAndRegistered = (modulePathLoader() as Promise<NgModuleFactory<WithCustomElementComponent>>)
|
||||
const loadedAndRegistered =
|
||||
(modulePathLoader() as Promise<NgModuleFactory<WithCustomElementComponent> | Type<WithCustomElementComponent>>)
|
||||
.then(elementModuleOrFactory => {
|
||||
/**
|
||||
* With View Engine, the NgModule factory is created and provided when loaded.
|
||||
* With Ivy, only the NgModule class is provided loaded and must be compiled.
|
||||
* This uses the same mechanism as the deprecated `SystemJsNgModuleLoader` in
|
||||
* in `packages/core/src/linker/system_js_ng_module_factory_loader.ts`
|
||||
* to pass on the NgModuleFactory, or compile the NgModule and return its NgModuleFactory.
|
||||
*/
|
||||
if (elementModuleOrFactory instanceof NgModuleFactory) {
|
||||
return elementModuleOrFactory;
|
||||
} else {
|
||||
return this.compiler.compileModuleAsync(elementModuleOrFactory);
|
||||
}
|
||||
})
|
||||
.then(elementModuleFactory => {
|
||||
const elementModuleRef = elementModuleFactory.create(this.moduleRef.injector);
|
||||
const injector = elementModuleRef.injector;
|
||||
|
|
Loading…
Reference in New Issue