如何在Angular 2中重新触发所有组件树上的所有纯管道 [英] How to re-trigger all pure pipes on all component tree in Angular 2

查看:104
本文介绍了如何在Angular 2中重新触发所有组件树上的所有纯管道的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个纯管道 TranslatePipe ,该管道使用具有 locale $的 LocaleService 来翻译短语:可观察的< string> 当前语言环境。我还为所有组件(包括 AppComponent )启用了 ChangeDetectionStrategy.OnPush 。现在,当有人更改语言时如何重新加载整个应用程序? (在 locale $ 中观察到新值)。



当前,我正在使用用户在语言之间切换后,location.reload()。这很烦人,因为整个页面都已重新加载。我该如何使用 pure 管道和 OnPush 检测策略来做到这一点?

解决方案

感谢GünterZöchbauer答案(请参阅评论),我明白了



在我不太了解的情况下,Angular的变更检测器的工作原理如下:

  cd.detectChanges(); //检测更改,但不更新视图。 
cd.markForCheck(); //将视图标记为检查,但未检测到更改。

因此您需要同时使用两者才能快速重建整个组件树。



1。模板更改



为了重新加载整个应用程序,我们需要隐藏并显示所有组件树,因此需要将所有内容包装在 app.component中。 html 放入 ng-容器

 < ng容器* ngIf =!reloading> 
< header>< / header>
< main>
< router-outlet>< / router-outlet>
< / main>
< footer>< / footer>
< / ng-container>

ng容器比div好,因为



对于异步支持,我们可以执行以下操作:

 < ng-container * ngIf =!(reloading $ | async)> ...< / ng-container> 

重装:布尔值 reloading $:这里的Observable< boolean> 表示当前正在重新加载组件。



在组件中,我有 LocaleService 具有可观察的语言$ 。我将收听更改语言的事件并执行应用程序重新加载操作。



2。同步示例



 导出类AppComponent实现了OnInit {
reloading:boolean;

构造函数(
私有cd:ChangeDetectorRef,
私有语言环境: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类AppComponent实现OnInit {
reloading:BehaviorSubject< boolean> ;;

获得重新加载$():Observable< boolean> {
返回this.reloading.asObservable();
}

构造函数(
private cd:ChangeDetectorRef,//我们仍然必须使用它。
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。路由器



路由器无效。以这种方式重新加载应用程序时,路由器出口内容将变为。我还没有解决这个问题,所以走同一条路线可能会很痛苦,因为这意味着用户对表单所做的任何更改都会被更改或丢失。



5。 OnInit



您必须使用OnInit挂钩。如果尝试在构造函数内部调用cd.detectChanges(),则会出现错误,因为angular尚未构建组件,但您将尝试检测其更改。



<现在,您可能会认为我在构造函数中订阅了另一个服务,并且只有在组件完全初始化后,我的订阅才会触发。但问题是-您不知道该服务的工作方式!例如,如果它仅发出值 Observable.of('en')-您将收到错误消息,因为一旦订阅-当component为



我的 LocaleService 也有同样的问题:observable后面的主题是 BehaviorSubject BehaviorSubject 是rxjs主题,在您订阅后立即发出 default 值。因此,一旦您编写 this.locale.language $ .subscribe(...)-订阅会立即触发至少一次,只有这样您才能等待语言更改。


I have pure pipe TranslatePipe that translates phrases using LocaleService that has locale$: Observable<string> current locale. I also have ChangeDetectionStrategy.OnPush enabled for all my components including AppComponent. Now, how can I reload whole application when someone changes language? (emits new value in locale$ observable).

Currently, I'm using location.reload() after user switches between languages. And that's annoying, because whole page is reloaded. How can I do this angular-way with pure pipe and OnPush detection strategy?

解决方案

Thanks to Günter Zöchbauer answer (see comments), I got it working.

As I understant, Angular's change detector works like this:

cd.detectChanges(); // Detects changes but doesn't update view.
cd.markForCheck();  // Marks view for check but doesn't detect changes.

So you need to use both in order to quickly rebuild whole component tree.

1. Template changes

In order to reload whole application we need to hide and show all component tree, therefore we need to wrap everything in app.component.html into ng-container:

<ng-container *ngIf="!reloading">
  <header></header>
  <main>
    <router-outlet></router-outlet>
  </main>
  <footer></footer>
</ng-container>

ng-container is better than div because it doesn't render any elements.

For async support, we can do something like this:

<ng-container *ngIf="!(reloading$ | async)"> ... </ng-container>

reloading: boolean and reloading$: Observable<boolean> here indicates that the component is currently being reloaded.

In the component I have LocaleService which has language$ observable. I will listen to changed language event and perform application reload action.

2. Sync example

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 example

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();
        });
    }
}

We don't have to cd.markForChanges() now but we still have to tell the detector to detect changes.

4. Router

Router doesn't work as expected. When reloading application in such fashion, router-outlet content will become empty. I did not resolve this problem yet, and going to the same route can be painful because this means that any changes user has made in forms, for example, will be altered and lost.

5. OnInit

You have to use the OnInit hook. If you try to call cd.detectChanges() inside of constructor, you will get an error because angular will not build component yet, but you will try to detect changes on it.

Now, you may think that I subscribe to another service in constructor, and my subscription will only fire after component is fully initialized. But the thing is - you don't know how the service works! If, for example, it just emits a value Observable.of('en') - you'll get an error because once you subscribe - first element emitted immediately while component is still not initialized.

My LocaleService has the very same issue: the subject behind observable is BehaviorSubject. BehaviorSubject is rxjs subject that emits default value immediately right after you subscribe. So once you write this.locale.language$.subscribe(...) - subscription immediately fires at least once, and only then you will wait for language change.

这篇关于如何在Angular 2中重新触发所有组件树上的所有纯管道的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆