13

Common menu usage case

<menu>
    <menu-item1></menu-item1>
    <menu-item2></menu-item2>
    <menu-item3></menu-item3>
</menu>

menu template

<div *ngIf="open$ | async">
    <ng-content></ng-content>
</div>

I was suprised to hear that all menu-item* components (and all their children) will be instantiated despite their presence in DOM and menu component *ngIf state. Their OnInit and AfterViewInit hooks will be called even if menu has never been opened and OnDestroy will never fires despite real adding-removing from DOM. Here is a closed issue about this https://github.com/angular/angular/issues/13921 (there is a plnkr with example) and an issue to angular documentation https://github.com/angular/angular.io/issues/3099.

But this issue is still here - how could i do so that menu items will be instantiated only when menu is opened and properly destroyed if closed? All hooks should fire only related to real DOM state.

drow
  • 194
  • 3
  • 15

2 Answers2

8

update Angular 5

ngOutletContext was renamed to ngTemplateOutletContext

See also https://github.com/angular/angular/blob/master/CHANGELOG.md#500-beta5-2017-08-29

original

You can use

<menu>
  <template>
    <menu-item1></menu-item1>
    <menu-item2></menu-item2>
    <menu-item3></menu-item3>
  <template>
</menu>
@Component({
  selector: 'menu',
  template: `
<div *ngIf="open$ | async">
  <template [ngTemplateOutlet]="templateRef"></template>
</div>
`
})
class MenuComponent {
  @ContentChild(TemplateRef) templateRef:TemplateRef;
}

You can also pass context to ngTemplateOutlet (there are some answers that show how to do that, I don't have time just not to look them up)

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    Hm, looks like it's easiest way to achieve expected transclusion, thx. There is a big lack in ng2 documentation about ` – drow Jan 14 '17 at 23:15
  • I collected the knowledge from various comments. I don't know about docs. – Günter Zöchbauer Jan 15 '17 at 08:09
  • 2
    After reading all related info on the topic, I feel that this unexpected behaviour is very dissapointing. The Angular Team can say: "it's by design", but its nowhere clear nor explicit in the Angular documentation. I'm sure there are thousands of Angular users that assume ngIf will work everywhere the same and have implemented lots of logic in the ngOnInit() as Angular guidelines tell, it's some kind of contradictory. Just a rant, thanks for the workarround Gunter! – Dani P. Nov 25 '20 at 11:06
0

This is an exemple using ngTemplateOutlet

import {Component, NgModule} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'

@Component({
  selector: 'my-app',
  template: `
    <template #templateRef let-label="label" let-url="url">
      <div><a href="{{url}}">{{label}}</a></div>
    </template>

    <div [ngTemplateOutlet]="templateRef" [ngOutletContext]="menu[0]"></div>
    <div [ngTemplateOutlet]="templateRef" [ngOutletContext]="menu[1]"></div>
  `
})
export class App {
    menu:any = [{
           "id": 1,
           "label": "AngularJS",
           "url": "http:\/\/www.learn-angular.fr\/angularJS"
          }, {
           "id": 2,
           "label": "Angular",
           "url": "http:\/\/www.learn-angular.fr\/angular"
    }];

    constructor() {}
}