10

Is it possible to Identify whether a Angular2 component (here AppComponent) is completely loaded ( including ViewChilds ) when there ngIf in template which conditionally loads the child.

Reference: Angular 2 @ViewChild annotation returns undefined This example is taken from the above reference. Thanks to kenecaswell

import {Component, ViewChild, OnInit, AfterViewInit} from 'angular2/core';
import {ControlsComponent} from './child-component-1';
import {SlideshowComponent} from './slideshow/slideshow.component';

@Component({
    selector: 'app',
    template:  `
        <div *ngIf="controlsOn">
            <controls ></controls>
            <slideshow></slideshow>
        </div>
    `,
    directives: [SlideshowComponent, ControlsComponent]
})

export class AppComponent {
    @ViewChild(ControlsComponent) controls:ControlsComponent;   
    @ViewChild(SlideshowComponent) slide:SlideshowComponent;

    controlsOn:boolean = false;

    ngOnInit() {
        console.log('on init', this.controls);
        // this returns undefined
    }

    ngAfterViewInit() {
        console.log('on after view init', this.controls);
        // this returns null
    }

}

The ngOnInit && ngAfterViewInit are fired before the the child components are loaded because of the ngIf condition

I need identify when SlideshowComponent & ControlsComponent are loaded and perform an action based on that.

I have a hacky solution which is not suitable when there are multiple ViewChilds (Which are of different type) - Using an event emitter to inform when the child is loaded.

I'm posting this question since there were no proper solution after hours of research.

tymspy
  • 4,200
  • 2
  • 20
  • 35

2 Answers2

2

PLUNKER

Try ViewChildren instead of ViewChild, which provides a changes Observable, we can use as a hook.

To track all the ViewChildren, you can merge their changes Observables into one and subscribe to it, then you get single point of action, like this

  @ViewChildren(ChildCmp) children: QueryList<ChildCmp>;
  @ViewChildren(AnotherChildCmp) anotherChildren: QueryList<ChildCmp>;

  childrenDetector: Observable<any>; // merged observable to detect changes in both queries

  ngAfterViewInit(){
    this.childrenDetector = Observable.merge(this.children.changes, this.anotherChildren.changes)

    this.childrenDetector.subscribe(() => {

      // here you can even check the count of view children, that you queried
      // eg:  if(this.children.length === 1 && this.anotherChildren.length === 1) { bothInitialized(); }
      // or something like that

      alert('you just initialized a children');
    });
  }
}
Ankit Singh
  • 24,525
  • 11
  • 66
  • 89
  • Hi, My problem is that I have 3 components (ViewChild) of different type and I want to identify when all three component (ViewChild) are loaded and perform an action. I have updated the question. Thanks – tymspy Jul 12 '16 at 11:04
  • 1
    OK, the issue is caused by `*ngIf` not letting the children components initialize on ViewInit, now `ViewChildren` makes even more sense, because it can tell you when the children initializes, see [this PLUNKER](http://plnkr.co/edit/HevkaqUyqmkAnvgPwm7v?p=preview) – Ankit Singh Jul 12 '16 at 11:18
  • Ok, I'm trying that now, does the changes observable inform all the changes ( the angular docs is incomplete ). – tymspy Jul 12 '16 at 11:28
  • Yes, it fires when the number of queried component changes – Ankit Singh Jul 12 '16 at 11:30
  • did it help, or there is something else ? – Ankit Singh Jul 12 '16 at 11:55
  • 1
    I think the ViewChildren, ContentChildren change streams are broken in 2.0.0 as per https://github.com/angular/angular/issues/9689 – user776686 Oct 13 '16 at 13:18
1

You can wrap stuff in *ngIf so that html won't show anything untill ngOnInit finished all.

<div *ngIf="loaded">
    /* all codes go here */
<div>

OnInit(){
    foo();
    bar(()=>{
        this.loaded = true;
    });
}
Peter Huang
  • 972
  • 4
  • 12
  • 34