angular: Transcluded components hooks works incorrectly with upper `*ngIf` state

I’m submitting a … (check one with “x”)

[ x ] bug report
[ ] feature request
[ ] support request

Current behavior Transcluded via ng-content components OnInit and AfterViewInit hooks are triggered despite parent component ngIf state. OnDestroy hook does not triggered on removing from DOM.

Expected behavior Hooks should be triggered only when component really are added/removed from DOM.

Minimal reproduction of the problem with instructions Plnkr http://plnkr.co/edit/RkOXyEtvMNoSNLeAh8Jf?p=preview

MenuItemComponent OnInit and AfterViewInit hooks triggered despite MenuComponent ngIf false value and OnDestroy never fires despite component removing from DOM.

  • Angular version: 2.4.3

update added one more plnkr - anything inside <menu> component will be initialized with all children despite it never will be added to dom due to closed menu. Comment bellow https://github.com/angular/angular/issues/13921#issuecomment-272589159 http://plnkr.co/edit/2aaRtK3rppO9Qi8zWaWi?p=preview

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 26 (19 by maintainers)

Most upvoted comments

I want to add this is definitely not expected and if not known and handled can cause huge performance issues in instances like tabs.

Looks like solution is to use <template> around menu items and TemplateRef/[ngTemplateOutlet] in menu component, thx to Günter http://stackoverflow.com/a/41655566/5803093

There is a big lack in ng2 documentation on this theme 😦

Also as i understand AfterViewInit guarantees that component is added to DOM here that is wrong with this transclusion behavior.

@montella1507 … yep, you are right in that. Some of those third party libs are not able to work on the nodes which are not in DOM. Then you have to add some your custom logic to control what to invoke and when … in relation if the node is or isn’t in DOM.

@montella1507 As “Angular application can be entirely bootstrapped in a detach element”, any component MUST NOT assume their host elements being mounted in document (if wanted to be robust). If your case require it being attached, then the component should guard that by itself.

@montella1507 … what is exactly component X?

Generally … if the template abc.component.html of the component ABC contains …

<div *ngIf="boolean">
  <Y> </Y>
</div>

then <div *ngIf="true"> would mean creating a new instance of component Y and inserting it to the DOM and with <div *ngIf="false"> it is reversed.

But with transcluded content, like …

<ABC> //ng-content inside 
  <Y (click)="foo()">  // transcluded
 </Y>
</ABC>

the instance of component Y is created in the same moment when the instance of the component ABC and it is kept regardless of the conditional logic and *ngIf applied on <ng-content> inside the template of ABC. All of that (generated JS code) is defined by Angular compiler when HTML templates are compiled (AOT / JIT) and not during run-time.

@mhevery so for any component there is no guarantees that init/destroy hooks will be related to component DOM existing state and depends on outer context that cant be detected from this component?

@mhevery em… but what with menu/tabs components if their items depends on some data that should be updated before menu is opened and has a lot of handlers in OnInit AfterViewInit OnDestroy hooks? Why this components should be init when they are not needed and should not do anything, that is controlled by upper ngIf.