为什么添加 Material 模块会破坏动态组件插入?
Why adding a Material Module breaks the dynamic component insertion?
这是 StackBlitz 上使用动态组件创建的两个 MCVE。我想明白为什么:
有人知道吗?
有效的示例
app.component.ts
import { Component, ChangeDetectionStrategy, OnDestroy, ChangeDetectorRef, ViewChild, ViewContainerRef, Input, Compiler, Injector, NgModuleRef, SimpleChanges, NgModule, ComponentFactoryResolver, ReflectiveInjector } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HttpClientModule } from '@angular/common/http';
import { MatButtonModule } from '@angular/material';
@Component({
selector: 'my-app',
template: `
<ng-container
#viewContainerRef
></ng-container>
`,
})
export class AppComponent {
@ViewChild('viewContainerRef', { read: ViewContainerRef }) viewContainerRef: ViewContainerRef;
constructor(
private compiler: Compiler,
private injector: Injector,
private ngModuleRef: NgModuleRef<any>,
private changeDetectorRef: ChangeDetectorRef,
) {
}
ngOnInit() {
const template = `
<button
mat-button
[innerHTML]="'Click me!'"
>
</button>
`
this.viewContainerRef.clear();
const component = Component({
changeDetection: ChangeDetectionStrategy.OnPush,
template
})(class { });
const ngModule = NgModule({
imports: [
CommonModule,
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
],
entryComponents: [component],
declarations: [component]
})(class { });
this.compiler.compileModuleAndAllComponentsAsync(ngModule).then((factories) => {
const factory = factories.componentFactories[0];
const componentRef = this.viewContainerRef.createComponent(
factory,
this.viewContainerRef.length,
this.injector,
[],
this.ngModuleRef
);
this.changeDetectorRef.detectChanges();
});
}
}
app.module.ts
import { NgModule, CompilerFactory, COMPILER_OPTIONS, Compiler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { JitCompilerFactory } from '@angular/platform-browser-dynamic';
import { AppComponent } from './app.component';
export function createCompiler(compilerFactory: CompilerFactory) {
return compilerFactory.createCompiler();
}
@NgModule({
imports: [ BrowserModule, FormsModule ],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ],
providers: [
{ provide: COMPILER_OPTIONS, useValue: {}, multi: true },
{ provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS] },
{ provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory] },
],
})
export class AppModule { }
无效的示例
将 MatButtonModule
添加到动态模块的导入中:
app.component.ts
const ngModule = NgModule({
imports: [
CommonModule,
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
MatButtonModule,
],
entryComponents: [component],
declarations: [component]
})(class { });
行为
没有报错,但是动态组件好像没有插入(至少是看不见的)
原因是你总是从数组中获取第一个编译工厂。
const factory = factories.componentFactories[0];
当您需要从您的组件获取工厂时:
const factory = factories.componentFactories.find(x => x.componentType === component);
这是 StackBlitz 上使用动态组件创建的两个 MCVE。我想明白为什么:
有人知道吗?
有效的示例
app.component.ts
import { Component, ChangeDetectionStrategy, OnDestroy, ChangeDetectorRef, ViewChild, ViewContainerRef, Input, Compiler, Injector, NgModuleRef, SimpleChanges, NgModule, ComponentFactoryResolver, ReflectiveInjector } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HttpClientModule } from '@angular/common/http';
import { MatButtonModule } from '@angular/material';
@Component({
selector: 'my-app',
template: `
<ng-container
#viewContainerRef
></ng-container>
`,
})
export class AppComponent {
@ViewChild('viewContainerRef', { read: ViewContainerRef }) viewContainerRef: ViewContainerRef;
constructor(
private compiler: Compiler,
private injector: Injector,
private ngModuleRef: NgModuleRef<any>,
private changeDetectorRef: ChangeDetectorRef,
) {
}
ngOnInit() {
const template = `
<button
mat-button
[innerHTML]="'Click me!'"
>
</button>
`
this.viewContainerRef.clear();
const component = Component({
changeDetection: ChangeDetectionStrategy.OnPush,
template
})(class { });
const ngModule = NgModule({
imports: [
CommonModule,
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
],
entryComponents: [component],
declarations: [component]
})(class { });
this.compiler.compileModuleAndAllComponentsAsync(ngModule).then((factories) => {
const factory = factories.componentFactories[0];
const componentRef = this.viewContainerRef.createComponent(
factory,
this.viewContainerRef.length,
this.injector,
[],
this.ngModuleRef
);
this.changeDetectorRef.detectChanges();
});
}
}
app.module.ts
import { NgModule, CompilerFactory, COMPILER_OPTIONS, Compiler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { JitCompilerFactory } from '@angular/platform-browser-dynamic';
import { AppComponent } from './app.component';
export function createCompiler(compilerFactory: CompilerFactory) {
return compilerFactory.createCompiler();
}
@NgModule({
imports: [ BrowserModule, FormsModule ],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ],
providers: [
{ provide: COMPILER_OPTIONS, useValue: {}, multi: true },
{ provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS] },
{ provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory] },
],
})
export class AppModule { }
无效的示例
将 MatButtonModule
添加到动态模块的导入中:
app.component.ts
const ngModule = NgModule({
imports: [
CommonModule,
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
MatButtonModule,
],
entryComponents: [component],
declarations: [component]
})(class { });
行为
没有报错,但是动态组件好像没有插入(至少是看不见的)
原因是你总是从数组中获取第一个编译工厂。
const factory = factories.componentFactories[0];
当您需要从您的组件获取工厂时:
const factory = factories.componentFactories.find(x => x.componentType === component);