我正在寻找一个实现以下案例的概念:
我有一个父搜索组件,它有一些组件作为视图子/内容子项,用于显示构面和搜索结果。我现在想在应用程序加载完成后触发搜索,这样用户就不会看到空页。
我现在的问题是我找不到符合我需要的生命周期钩子。构面/搜索结果在各自的ngOnInit中订阅搜索结果。所以我需要一个在所有子组件完成初始化之后被称为的钩子。
我在父组件
上尝试了以下挂钩任何想法如何解决这个问题?对我来说,似乎我没有掌握一些基本的东西。
干杯
汤姆
答案 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)
在我的情况下,我需要等待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;。