所有孩子初始化后的Angular 2生命周期钩子?

时间:2016-08-04 09:16:17

标签: angular

我正在寻找一个实现以下案例的概念:

我有一个父搜索组件,它有一些组件作为视图子/内容子项,用于显示构面和搜索结果。我现在想在应用程序加载完成后触发搜索,这样用户就不会看到空页。

我现在的问题是我找不到符合我需要的生命周期钩子。构面/搜索结果在各自的ngOnInit中订阅搜索结果。所以我需要一个在所有子组件完成初始化之后被称为的钩子。

我在父组件

上尝试了以下挂钩
  • ngAfterContentInit :在孩子们调用ngOnInit之前调用它
  • ngAfterViewInit :这个有效,但在搜索结果返回后,子视图会更新,这会导致错误,因为 ngAfterViewInit

任何想法如何解决这个问题?对我来说,似乎我没有掌握一些基本的东西。

干杯

汤姆

7 个答案:

答案 0 :(得分:37)

我最终以下列方式使用ngAfterViewInit()钩子:

ngAfterViewInit(){
  //stuff that doesn't do view changes
  setTimeout(_=> this.methodThatKicksOffAnotherRoundOfChanges());
}

与设置实际时间的setTimeout的其他调用相比,这应该是安全的,因为它应该立即启动(并且只影响线程/上下文行为)

答案 1 :(得分:2)

让子组件在其Init上发出事件并在需要的地方进行侦听

答案 2 :(得分:2)

如果您还有内容子项(使用<ng-content>进行了转换),则ngAfterContentInit()是正确的生命周期回调。

https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#aftercontent

ngAfterContentInit() {
  doSomething();
}

答案 3 :(得分:1)

OP似乎认为它并不适合他的需要,或者不优雅,但似乎Vale Steve的回答是最符合逻辑的方法。

在我的情况下,我需要等待2个子组件进行初始化,这可以控制我的应用程序中的左侧和右侧面板。我有两个子组件声明它们初始化为共享服务:

  constructor(
      public _navigate: RouteNavigationService // service shared between all child components and parent
  ){}

  ngOnInit() {
    this._navigate.setChildInit(this.panel);
  }

共享服务(RouteNavigationService):

  private _leftChildInit  = new Subject();
  private _rightChildInit = new Subject();

  leftChildInit$          = this._leftChildInit.asObservable();
  rightChildInit$         = this._rightChildInit.asObservable();

  setChildInit(panel){
    switch(panel){
      case 'leftPanel':
        console.log('left panel initialized!');
        this._leftChildInit.next(true);
        break;
      case 'rightPanel':
        console.log('right panel initialized!');
        this._rightChildInit.next(true);
        break;
    }
  }

然后在我的父组件中,我使用zip方法将多个Observable组合在一起(您可以在这里添加其他子组件)并等待它们全部完成:

  childInitSub: Subscription;

  constructor(
      public _navigate: RouteNavigationService
  ) {
    this.childInitSub = Observable.zip( // WAIT FOR BOTH LEFT & RIGHT PANELS' ngOnInit() TO FIRE
        this._navigate.leftChildInit$,
        this._navigate.rightChildInit$).subscribe(data =>
        // Both child components have initialized... let's go!
    );
  }

OP在评论中说明

  

&#34;我不喜欢这样,因为每次我添加一个我需要的新组件   等待额外的onInit事件,并且必须实现它&#34;

但实际上,你要做的就是在你的服务中为新的子组件ngOnInit创建另一个Subject,并在父组件zip方法中添加一个额外的行。

请注意,此处使用ngAfterViewInit()接受的答案并非与完全相同。在我的情况下,使用ngAfterViewInit()产生ExpressionChangedAfterItHasBeenCheckedError超出了这个问题的范围,但是用来证明这种方法实际上并没有做同样的事情。

相比之下,上述方法实际上只是触发作为所有子组件的直接结果。 ngOnInit()已解雇的事件

答案 4 :(得分:1)

如果你需要你的父组件等待跨多个子组件的自定义异步行为,那么这是我做的一个很好的解决方案。

任何异步初始化的子组件都会扩展它:

import {Subject} from 'rxjs/Subject';
import {Observable} from 'rxjs/Observable';

export class AsynchronouslyInitialisedComponent {
  loadedState: Subject<boolean> = new Subject<boolean>();
  loadedState$ = this.loadedState.asObservable();
  constructor() {
  }
  protected componentLoaded() {
    this.loadedState.next(true);
  }
}

示例子组件:

import {Component} from '@angular/core';
import {AsynchronouslyInitialisedComponent} from './../base_component';

@Component({
  moduleId: module.id,
  selector: 'child'
})
export class Child extends AsynchronouslyInitialisedComponent {
  constructor() {
    super();
  }
  ngOnInit() {
    //Some async behavoir:
    setTimeout(() => {
      this.componentLoaded();
    }, 10000);
  }
}

现在您所有的父组件需要做的是:

import {Component, ViewEncapsulation, ViewChild};
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/zip';

@Component({
  moduleId: module.id,
  selector: 'parent'
})
export class Parent { 

  @ViewChild('child') child; //Just use <child #child></child> in your template

  constructor() {
  }
  ngOnInit() {
    Observable
    .zip(this.child.loadedState$, this.otherChild.loadedState$) //Add as many as you want here... 
    .subscribe(pair => {
      console.log('All child components loaded');
    });
  }
}

答案 5 :(得分:-1)

您可以尝试一下。对我有用

async ngOnInit() {
    // method to call
   await this.myMethod(); 
  }
}

答案 6 :(得分:-2)

在开发模式下(默认),更改检测机制会对组件树执行额外检查,以确定您不会在挂钩中操纵UI。这就是您收到错误的原因。

如果您确定ngAfterViewInit中UI的更改有效,请在bootstrap()之前调用enableProdMode()。这告诉Angular,&#34;不要在更改检测期间进行第二次传递&#34;。