Reputation: 3612
I am trying to display a dynamic component similar (not exact) to the example in angular docs.
I have a dynamic directive with viewContainerRef
@Directive({
selector: '[dynamicComponent]'
})
export class DynamicComponentDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
Excerpt from component code
@ViewChild(DynamicComponentDirective) adHost: DynamicComponentDirective;
..
ngAfterViewInit() {
let componentFactory = null;
console.log(component);
componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
// this.adHost.viewContainerRef.clear();
const viewContainerRef = this.adHost.viewContainerRef;
viewContainerRef.createComponent(componentFactory);
}
Finally added <ng-template dynamicComponent></ng-template>
in template
Upvotes: 33
Views: 40355
Reputation: 720
In my case, there was a typo in the directive selector on the ng-template
, and thus it did not match with the one declared in the directive, thus ViewContainerRef was undefined
. Simple cause, it was easy to overlook at first glance, as the error message may suggest having a more complex problem.
Upvotes: 0
Reputation: 1
I just added the viewContainerRef stuff in the afterViewInit() cicle instead of the onInit() cicle, it worked for me :T
I am using Angular v11
ngAfterViewInit(): void {
const viewContainerRef = this.exampleHost.viewContainerRef;
viewContainerRef.clear();
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(ExampleEntryComponent);
viewContainerRef.createComponent(componentFactory);
}
Upvotes: 0
Reputation: 1535
Template HTML file:
<ng-template #containerToHostDynamicallyInjectedComponents"></ng-template>
TS file
private componentRef: ComponentRef<COMPONENT_NAME_TO_CREATE> = null;
@ViewChild('containerToHostDynamicallyInjectedComponents', {read: ViewContainerRef}) hostContainer: ViewContainerRef;
constructor(private componentFactoryResolver:ComponentFactoryResolver) {}
ngOnInit() {
const componentFactory =
this.componentFactoryResolver.resolveComponentFactory(<COMPONENT_NAME_TO_CREATE>);
this.hostContainer.clear(); // CLEAR ANY INJECTED COMPONENT IF EXISTED.
this.componentRef = this.hostContainer.createComponent(componentFactory);
}
```
ACESSS COMPONENT PROPERTIES AND METHODS USING:
this.componentRef.instance.<PROPERTY_NAME>
this.componentRef.instance.<METHOD_NAME>();
YOU CAN EVEN SUBSCRIBE TO @Output EVENT EMITTERS
this.componentRef.instance.<OUTPUT_PROPERTY_NAME>
.pipe(
take(1)
)
.subscribe(response => console.log(response));
Upvotes: 1
Reputation: 1333
In my case, it was Angular 9 and the directive's selector was inside *ngIf
. The solution I used was:
@ViewChild(ExampleDirective, {static: false}) exampleDirectiveHost: ExampleDirective;
Mark static
is false due to reasons defined here Angular documentation
The other thing I did was use
ngAfterViewInit(){
const viewContainerRef = this.exampleDirectiveHost.viewContainerRef;;
}
Upvotes: 3
Reputation: 908
I ran into this problem as well and the reason was that the location which I wanted to load my component dynamicly into was inside an ng-if that was hidden initially.
<div *ngIf="items">
<ng-template appInputHost></ng-template>
</div>
@ViewChild(InputHostDirective, { static: true }) inputHost: InputHostDirective;
Moving the ng-template to outside the ng-if solved the problem.
Upvotes: 10
Reputation: 99
You see this error when the directive does not construct. You can set a breakpoint on the directive's constructor and check if the breakpoint ever hits. If not, that means that you are not loading the directive correctly. Then you can check that your component that is loading the directive it is properly adding the directive into the template.
Upvotes: -1
Reputation: 1
The problem might also be that the selector of the viewRef
(dynamicComponent
in this example) does not match the one specified in the template of the component which uses the componentFactoryResolver
.
Upvotes: 0
Reputation: 311
In Angular 8 my fix was:
@ViewChild('dynamicComponent', {static: true, read: ViewContainerRef}) container: ViewContainerRef;
Upvotes: 18
Reputation: 51
Noted that I face the same problem if directive selector (dynamicComponent in this case) is at :
first element of the component
parent element with *ngIf condition
Hence, I avoid it by put it inside at non-root tag in the component html & load component to viewContainerRef only when the condition match.
Upvotes: 2
Reputation: 421
I had the same problem. You have to add the directive into the AppModule:
@NgModule({
declarations: [
AppComponent,
...,
YourDirective,
],
imports: [
...
],
providers: [...],
bootstrap: [AppComponent],
entryComponents: [components to inject if required]
})
Upvotes: 42
Reputation: 485
You can take this approach:
don't create directive, instead give an Id to ng-template
<ng-template #dynamicComponent></ng-template>
use @ViewChild
decorator inside your component class
@ViewChild('dynamicComponent', { read: ViewContainerRef }) myRef
ngAfterViewInit() {
const factory = this.componentFactoryResolver.resolveComponentFactory(component);
const ref = this.myRef.createComponent(factory);
ref.changeDetectorRef.detectChanges();
}
Upvotes: 36