Nitsan Baleli
Nitsan Baleli

Reputation: 5458

@ViewChild(CdkPortalOutlet) returns undefined in AfterViewInit

Trying to query a ng-template with CdkPortalOutlet is always unsuccessful, and I cant understand why?

<ng-template CdkPortalOutlet></ng-template>

@ViewChild(CdkPortalOutlet) test: CdkPortalOutlet;

stackblitz

Upvotes: 3

Views: 2671

Answers (2)

j3ff
j3ff

Reputation: 6099

Since Angular 8 @ViewChild requires to give an extra static parameter and the documentation is hard to find on what value to set it to. If not set correctly it will result in an undefined value when trying to retrieve CdkPortalOutlet with @ViewChild.

TLDR

Set @ViewChild(CdkPortalOutlet, { static: true }) when migrating to Angular 8.

Extracted comment from Angular code

How do I choose which static flag value to use: true or false?

We have always recommended retrieving query results in ngAfterContentInit for content queries. This is because by the time this lifecycle hook runs, change detection has completed for the relevant nodes and we can guarantee that we have collected all the possible query results.

Most applications will want to use {static: false} for this reason. This setting will ensure query matches that are dependent on binding resolution (e.g. results inside *ngIfs or *ngFors) will be found by the query. If you need access to a TemplateRef in a query to create a view dynamically, you won't be able to do so in ngAfterContentInit. Change detection has already run on that view, so creating a new view with the template will cause an ExpressionHasChangedAfterChecked error to be thrown. In this case, you will want to set the static flag to true and create your view in ngOnInit. In most other cases, the best practice is to use {static: false}.

However, to facilitate the migration to version 8, you may also want to set the static flag to true if your component code already depends on the query results being available some time before ngAfterContentInit. For example, if your component relies on the query results being populated in the ngOnInit hook or in @Input setters, you will need to either set the flag to true or re-work your component to adjust to later timing.

Note: Selecting the static option means that query results nested in *ngIf or *ngFor will not be found by the query. These results are only retrievable after change detection runs.

Upvotes: 8

yurzui
yurzui

Reputation: 214175

In order to use CdkPortalOutlet directive in AppComponent template you have import PortalModule in AppModule(i.e. NgModule where AppComponent has been declared)

import { PortalModule } from '@angular/cdk/portal';
...

@NgModule({
  imports:      [ BrowserModule, FormsModule, PortalModule, OverlayModule ],
                                              ^^^^^^^^^^^^
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ]
})

export class AppModule { }

Also the Angular HTML parser is case sensitive so that you need to use it like:

<ng-template cdkPortalOutlet></ng-template>
             ^^^
          lower case

Forked Stackblitz

Upvotes: 8

Related Questions