问题
I'm building a small reference application to see how to dynamically inject components in one component to view the content on the page. I'm getting an error that points to the ViewContainerRef object.
This is the component that should display the injected component's content in the view, but it's throwing an error:
Here is the StatsComponent that is throwing the error:
export class StatsComponent implements AfterViewInit, OnDestroy {
@Input() dynComp: DynamicComponent;
@ViewChild(ComponentHostDirective) appComponentHost: ComponentHostDirective;
componentRef: ComponentRef<any>;
constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
ngAfterViewInit() {
this.loadComponent();
}
ngOnDestroy() {
this.componentRef.destroy();
}
loadComponent() {
console.log('inside stats component: ', this.dynComp);
const comp = this.dynComp;
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(comp.component);
console.log('host: ', this.appComponentHost); // <-- this is undefined
const viewContainerRef = this.appComponentHost.viewContainerRef;
viewContainerRef.clear();
this.componentRef = viewContainerRef.createComponent(componentFactory);
(<DynamicComponent>this.componentRef.instance).data = comp.data;
}
}
I have a working demo here, and a github project.
Why is the container not being referenced?
[UPDATE]: this now works! Go to my demo and github project to see it in action.
回答1:
Angular can't recognize @ViewChild(ComponentHostDirective)
in your template because you didn't include ComponentHostDirective
directive to list of directives that are used to compile StatsComponent
:
To understand which directives angular uses to compile angular template take a look at this answer Angular 2 Use component from another module (see diagram)
I know you have already declared ComponentHostDirective
within AppModule
. But your StatsComponent
is declared within HomeModule
and this module knows nothing about ComponentHostDirective
. We have to declare or import this directive in HomeModule
.
If we will declare ComponentHostDirective
in HomeModule
we get the error
Type ComponentHostDirective is part of the declarations of 2 modules: AppModule and HomeModule!
SharedModule
comes to the rescue:
src/app/shared/shared.module.ts
@NgModule({
imports: [
CommonModule
],
declarations: [
ComponentHostDirective
],
exports: [
CommonModule,
ComponentHostDirective
]
})
export class SharedModule {}
app.module.ts
@NgModule({
declarations: [
ComponentHostDirective <== remove it
],
imports: [
...
SharedModule, // add this
...
]
})
export class AppModule { }
home.module.ts
@NgModule({
imports: [
...
SharedModule, // add this
...
]
})
export class HomeModule { }
After that your appComponentHost
property will refer to ComponentHostDirective
instance.
In addition, you will get the error
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'. Current value: 'Customers'.
after component creation.
Don't change bindings within ngAfterViewInit
hook. Use ngOnInit
instead:
export class StatsComponent implements OnInit, OnDestroy {
...
ngOnInit() {
this.loadComponent();
}
The article Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError error explains the behavior in great details (Thank's @Maximus).
Finally you will see the result:
回答2:
small buglet:
this.componentRef = viewContainerRef.createComponent(componentFactory);
should be:
let componentRef = viewContainerRef.createComponent(componentFactory);
don't add it on the component. and use let instead of const.
来源:https://stackoverflow.com/questions/45516705/angular-dynamic-component-injection-error