感谢 Günter Zöchbauer answer(参见 cmets),我得到了它的工作。
据我了解,Angular 的变化检测器是这样工作的:
cd.detectChanges(); // Detects changes but doesn't update view.
cd.markForCheck(); // Marks view for check but doesn't detect changes.
所以你需要同时使用这两者才能快速重建整个组件树。
1。模板更改
为了重新加载整个应用程序,我们需要隐藏和显示所有组件树,因此我们需要将 app.component.html 中的所有内容包装到 ng-container 中:
<ng-container *ngIf="!reloading">
<header></header>
<main>
<router-outlet></router-outlet>
</main>
<footer></footer>
</ng-container>
ng-container 比 div 更好,因为它不渲染任何元素。
对于异步支持,我们可以这样做:
<ng-container *ngIf="!(reloading$ | async)"> ... </ng-container>
这里的reloading: boolean和reloading$: Observable<boolean>表示当前正在重新加载组件。
在我有LocaleService 的组件中,language$ 是可观察的。我将监听更改的语言事件并执行应用程序重新加载操作。
2。同步示例
export class AppComponent implements OnInit {
reloading: boolean;
constructor(
private cd: ChangeDetectorRef,
private locale: LocaleService) {
this.reloading = false;
}
ngOnInit() {
this.locale.language$.subscribe(_ => {
this.reloading = true;
this.cd.detectChanges();
this.reloading = false;
this.cd.detectChanges();
this.cd.markForCheck();
});
}
}
3。异步示例
export class AppComponent implements OnInit {
reloading: BehaviorSubject<boolean>;
get reloading$(): Observable<boolean> {
return this.reloading.asObservable();
}
constructor(
private cd: ChangeDetectorRef, // We still have to use it.
private locale: LocaleService) {
this.reloading = new BehaviorSubject<boolean>(false);
}
ngOnInit() {
this.locale.language$.subscribe(_ => {
this.reloading.next(true);
this.cd.detectChanges();
this.reloading.next(false);
this.cd.detectChanges();
});
}
}
我们现在不必cd.markForChanges(),但我们仍然需要告诉检测器检测变化。
4。路由器
路由器无法正常工作。以这种方式重新加载应用程序时,router-outlet 的内容将变为 empty。我还没有解决这个问题,走同样的路可能会很痛苦,因为这意味着用户在表单中所做的任何更改都会被更改并丢失。
5。 OnInit
您必须使用 OnInit 挂钩。如果你尝试在构造函数中调用 cd.detectChanges() ,你会得到一个错误,因为 Angular 还不会构建组件,但你会尝试检测它的变化。
现在,您可能认为我在构造函数中订阅了另一个服务,并且我的订阅只有在组件完全初始化后才会触发。但问题是 - 你不知道服务是如何运作的!例如,如果它只发出一个值 Observable.of('en') - 你会收到一个错误,因为一旦你订阅 - 第一个元素立即发出,而组件仍未初始化。
我的LocaleService 也有同样的问题:observable 后面的主题是BehaviorSubject。 BehaviorSubject 是 rxjs 主题,在您订阅后立即发出 default 值。所以一旦你写了this.locale.language$.subscribe(...) - 订阅会立即触发至少一次,然后你才会等待语言更改。