2017-01-26 1 views
6

У меня есть чистая труба TranslatePipe, которая переводит фразы с использованием LocaleService, которая имеет locale$: Observable<string> текущий язык. У меня также есть ChangeDetectionStrategy.OnPush для всех моих компонентов, включая AppComponent. Теперь, как я могу перезагрузить целое приложение, когда кто-то меняет язык? (испускает новое значение в locale$ наблюдаемым).Как перезапустить все чистые трубы на все дерево компонентов в Angular 2

В настоящее время я использую location.reload() после того, как пользователь переключится между языками. И это раздражает, потому что вся страница перезагружается. Как я могу сделать это угловым способом с чистой труба и OnPush стратегия обнаружения?

ответ

5

Благодаря Гюнтер Zöchbauer answer (см. комментарии), я заработал.

Как я understant, детектор изменения углового работает так:

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. Aync пример

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

Вы должны использовать OnInit крюк. Если вы попытаетесь вызвать cd.detectChanges() внутри конструктора, вы получите сообщение об ошибке, поскольку угловой компонент еще не будет создан, но вы попытаетесь обнаружить изменения на нем.

Теперь вы можете подумать, что я подписываюсь на другую службу в конструкторе, и моя подписка будет срабатывать только после полной инициализации компонента. Но дело в том, что вы не знаете, как работает сервис! Если, например, он просто испускает значение Observable.of('en') - вы получите сообщение об ошибке, потому что, как только вы подписались - первый элемент испустит немедленно, пока компонент все еще не инициализирован.

У моей LocaleService есть такая же проблема: предметом наблюдения является BehaviorSubject. BehaviorSubject является субъектом rxjs, который испускает по умолчанию значение сразу сразу после подписания. Поэтому, как только вы пишете this.locale.language$.subscribe(...) - подписка сразу срабатывает хотя бы один раз, и только после этого вы будете ждать изменения языка.

+0

Вы можете использовать ' '. Он был добавлен, чтобы иметь возможность использовать более распространенный синтаксис, но все же с тем же поведением, что и элемент '