Благодаря ответу Гюнтеру Цохбауэру (см. комментарии), у меня все заработало.
Насколько я понимаю, детектор изменений 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
содержимое станет пустым. Я еще не решил эту проблему, и переход по тому же маршруту может быть болезненным, потому что это означает, что любые изменения, сделанные пользователем, например, в формах, будут изменены и потеряны.
5. При инициализации
Вы должны использовать хук OnInit. Если вы попытаетесь вызвать cd.detectChanges() внутри конструктора, вы получите ошибку, потому что angular еще не соберет компонент, но вы попытаетесь обнаружить в нем изменения.
Теперь вы можете подумать, что я подписываюсь на другую службу в конструкторе, и моя подписка сработает только после полной инициализации компонента. Но дело в том, что вы не знаете, как работает сервис! Если, например, он просто выдает значение Observable.of('en')
- вы получите ошибку, потому что как только вы подпишитесь - первый элемент выдается сразу, пока компонент еще не инициализирован.
У моего LocaleService
та же проблема: субъект, стоящий за наблюдаемым, — BehaviorSubject
. BehaviorSubject
— это тема rxjs, которая выдает значение по умолчанию сразу после подписки. Итак, как только вы напишете this.locale.language$.subscribe(...)
- подписка сразу сработает хотя бы один раз, и только потом вы будете ждать смены языка.
person
EwanCoder
schedule
26.01.2017