Search code examples
angularangular-materialangular-directive

Angular 9 Load Dynamic components with viewContainerRef gets undefined result


I have created an application that uses views within views, and I am having trouble to move my code into libraries, and then share the code.

My main page uses routing with loadChildren() to then actually load the component to view. My pages all share a common navigation component that provides the title bar, side menu, auto hiding, resizing, etc., via monitoring observables.

So my routing therefore has the main path be the NavigationComponent, which is the simple Angular Material Navigation schematic. This then has a router-outlet, which will then display my destination component.

My main pages are then the Material dashboard schematics. These show cards that have information in them and routing to standard URLs to load the actual component.

For instance, my home page has the navigation, and then a series of cards to show different data (About the site, Events, News, etc.)

const routes:Routes = [
    { path: '', component: NavigationCommonComponent,
        children: [
            { path: '', component: HomeComponent },
            { path: 'aboutus', component: AboutComponent },
            { path: 'events', component: EventsComponent },
            { path: 'news', component: NewsComponent },
        ]
    }
];

The basic routing above would give some information. When these are simply cards, everything works fine. I have moved the cards into a library, and data can be displayed on the card. What I am trying to do now, is actually show the content of the components in the card data.

For instance, the if the card was hard coded, you would use

<mat-card>
    <appEvents></AppEvents>
</mat-card>

This would then use the EventsController to display whatever in the card. Hard coded, this works. What I am trying to do now is use the same viewComponentRef examples from the Angular 9 documents, so that these cards are loaded dynamically.

For instance, I now have an array of data that has the component (Type) as the item to be viewed. I have a card component that is the

I have used the AdService from the examples:

@Injectable()
export class AdService {
    public getAds() {
        return [
            new AdItem(HeroJobAdComponent, {name: 'Bombasto', bio: 'Brave as they come'}),
            new AdItem(HeroJobAdComponent, {name: 'Dr IQ', bio: 'Smart as they come'}),

        ];
    }
}

The HeroJobAdComponent

@Component({
template: `
    <div class="job-ad">
    <h4>{{data.headline}}</h4>

    {{data.body}}
    </div>
`
})
export class HeroJobAdComponent implements AdComponent {
@Input() public data:any;

}

and the provided directive.

@Directive({
selector: '[appAdHost]',
})
export class AdDirective {
constructor(public viewContainerRef:ViewContainerRef) { }
}

Everything appears to be correct, except when the code is running, the viewContainerRef from the ViewChild:

@ViewChild(AdDirective, {static: true, read: ViewContainerRef}) public adHost:AdDirective;

appears to be null in the const viewContainerRef = this.adHost.viewContainerRef; line. The adHost is undefined.

public loadComponent() {
    this.currentAdIndex = (this.currentAdIndex + 1) % this.ads.length;
    const adItem = this.ads[this.currentAdIndex];

    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(adItem.component);

    const viewContainerRef = this.adHost.viewContainerRef;
    viewContainerRef.clear();

    const componentRef = viewContainerRef.createComponent(componentFactory);
    (componentRef.instance as AdComponent).data = adItem.data;
}

I believe this has to do with the levels of the components, where I have the grid/card list (material dashboard schematic) as a child of my navigation component. I do not know how to get this element to not be undefined.


Solution

  • drop the read: ViewContainerRef property.