Instantiate transcluded components depending on their DOM state in angular 2
Asked Answered
B

2

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.

Brachypterous answered 14/1, 2017 at 17:33 Comment(0)
Y
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)

Yesima answered 14/1, 2017 at 22:15 Comment(3)
Hm, looks like it's easiest way to achieve expected transclusion, thx. There is a big lack in ng2 documentation about <template>/TemplateRef/ngTemplateOutlet and other things that looks strange and unobvious. Can u recommend any text/video content on this theme?Brachypterous
I collected the knowledge from various comments. I don't know about docs.Ohara
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!Smetana
I
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() {}
}
Inconvenient answered 16/1, 2017 at 13:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.