@ContentChild is null for DynamicComponentLoader

I have a side navigation component that will be used on many pages. This is not a one page web application. It is dynamically loaded on the parent component, which switches the menu. ContentChild is used to get the handle of the child component so that it can be switched by setting @input overlayHidden. The problem is that ContentChild is null.

Run this plunker with the debug console open. Click “Control Panel” from the first page, and then “Switch Menu” from the second page to see the null pointer.

What am I doing wrong?

Here is the code.

import { Component, Input} from 'angular2/core'; import { Router } from 'angular2/router'; @Component({ selector: 'side-navigation', template: ` <div id="overlay" [hidden]="overlayHidden"> <div id="wrapper"> <nav id="menu" class="white-bg active" role="navigation"> <ul> <li><a href="#">Dashboard</a></li> <li><a href="#">Help & FAQs</a></li> <li><a href="#">Contact Us</a></li> <li><a href="#">Log Out</a></li> </ul> </nav> </div> </div> ` }) export class SideNavigationComponent { @Input() overlayHidden: boolean; } import { Component, DynamicComponentLoader, Injector, ContentChild, AfterContentInit } from 'angular2/core'; import { Router, RouterLink } from 'angular2/router'; import { SideNavigationComponent } from './side-navigation'; @Component({ selector: 'dashboard', template: ` <side-navigation id="side-navigation"></side-navigation>... `, directives: [ RouterLink, SideNavigationComponent ] }) export class DashboardComponent { title = 'Dashboard'; @ContentChild(SideNavigationComponent) sideNavigationComponent: SideNavigationComponent; constructor(private _router: Router, private _dcl: DynamicComponentLoader, private _injector: Injector) { this._router = _router; this._dcl = _dcl; this._injector = _injector; this._dcl.loadAsRoot( SideNavigationComponent, '#side-navigation', this._injector); } toggleOverlay() { this.overlayHidden = !this.overlayHidden; this.sideNavigationComponent.overlayHidden = this.overlayHidden; } } 

I built a publishing / signing solution that works (except for the problem with the hidden attribute of the overlay div), but I thought it would be a simpler solution if I could get it working.

+1
source share
2 answers

As Gunter suggested in his answer about @ContentChild and besides, you can think about it,
As discussed with Günter - see plunker
Sorry, I did not go deep into your plunker and did not do what you want to achieve next. But if you are going to implement the ViewChild api, consider implementing an AfterViewInit hook. And it also works with DynamicComponentLoader .

+1
source

There are several problems

  • LoadAsRoot does not trigger change detection

Currently, loadAsRoot() used only to load the root component ( AppComponent ), and inputs are not supported in the root component.

The workaround is explained in this comment https://github.com/angular/angular/issues/6370#issuecomment-193896657

when using loadAsRoot you need to initiate change detection and manually connect the inputs, outputs, injector and component layout function

 function onYourComponentDispose() { } let el = this.elementRef let reuseInjectorOrCreateNewOne = this.injector; this.componentLoader.loadAsRoot(YourComponent, this.elementRef, reuseInjectorOrCreateNewOne, onYourComponentDispose) .then((compRef: ComponentRef) => { // manually include Inputs compRef.instance['myInputValue'] = {res: 'randomDataFromParent'}; // manually include Outputs compRef.instance['myOutputValue'].subscribe(this.parentObserver) // trigger change detection cmpRef.location.internalElement.parentView.changeDetector.ref.detectChanges() // always return in a promise return compRef }); 

This comment https://github.com/angular/angular/issues/7453#issuecomment-193138577 also has a link to Plunker example workaround demonstration

  • DynamicComponentLoader cannot be used in constructor() . This code should be moved to ngAfterViewInit() :

    this._dcl.loadAsRoot (SideNavigationComponent, '# side-navigation', this._injector);

  • as @Miconyks pointed out, @ViewChild() should be used instead of @ContentChild() . @ContentChild() for content passed to <ng-content>

0
source

Source: https://habr.com/ru/post/1246466/


All Articles