如何重新触发Angular 2中所有组件树上的所有纯管道

Ewa*_*der 17 angular2-changedetection angular2-pipe angular

我有纯管道TranslatePipe,使用LocaleService具有locale$: Observable<string>当前区域设置的短语进行翻译.我也ChangeDetectionStrategy.OnPush启用了所有组件,包括AppComponent.现在,当有人更改语言时,如何重新加载整个应用程序?(在locale$observable中发出新值).

目前,我location.reload()在用户在语言之间切换后使用.这很烦人,因为整个页面都重新加载.如何使用管道和OnPush检测策略进行这种角度方式?

Gün*_*uer 19

纯管道仅在输入值更改时触发.

您可以添加您修改的人工附加参数值

@Pipe({name: 'translate'})
export class TranslatePipe {
  transform(value:any, trigger:number) {
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

然后像使用它一样

<div>{{label | translate:dummyCounter}}</div>
Run Code Online (Sandbox Code Playgroud)

每当dummyCounter更新时,都会执行管道.

您还可以将区域设置作为附加参数而不是计数器传递.我不认为使用|async单个管道参数会起作用,因此这可能有点麻烦(需要分配给一个可用作管道参数的字段)


Ewa*_*der 10

感谢GünterZöchbauer的回答(见评论),我得到了它的工作.

据我所知,Angular的变化探测器的工作原理如下:

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

因此,您需要使用它们才能快速重建整个组件树.

1.模板更改

为了重新加载整个应用程序,我们需要隐藏并显示所有组件树,因此我们需要将所有内容包装app.component.htmlng-container:

<ng-container *ngIf="!reloading">
  <header></header>
  <main>
    <router-outlet></router-outlet>
  </main>
  <footer></footer>
</ng-container>
Run Code Online (Sandbox Code Playgroud)

ng-container 比div好,因为它不呈现任何元素.

对于异步支持,我们可以这样做:

<ng-container *ngIf="!(reloading$ | async)"> ... </ng-container>
Run Code Online (Sandbox Code Playgroud)

reloading: booleanreloading$: 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();
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

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();
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

我们现在没有,cd.markForChanges()但我们仍然必须告诉检测器检测变化.

4.路由器

路由器无法按预期工作.以这种方式重新加载应用程序时,router-outlet内容将变为.我还没有解决这个问题,并且走同一条路线可能会很痛苦,因为这意味着用户在表单中所做的任何更改都会被更改和丢失.

5. OnInit

你必须使用OnInit钩子.如果你试图在构造函数中调用cd.detectChanges(),你会收到一个错误,因为angular还没有构建组件,但你会尝试检测它的变化.

现在,您可能认为我在构造函数中订阅了另一个服务,而我的订阅只会在组件完全初始化后触发.但问题是 - 你不知道服务是如何运作的!例如,如果它只是发出一个值Observable.of('en')- 你会得到一个错误,因为一旦你订阅 - 第一个元素立即发出,而组件仍未初始化.

LocaleService有同样的问题:可观察的主题是BehaviorSubject.BehaviorSubject是订阅后立即发出默认值的rxjs主题.所以一旦你写this.locale.language$.subscribe(...)- 订阅立即触发至少一次,然后你才会等待语言变化.


Jud*_*ell 10

最佳性能解决方案:

我找到了解决方案.我讨厌称它为解决方案,但它确实有效.

我和orderBy管道有同样的问题.我在这里尝试了所有的解决方案,但性能影响很糟糕.

我只是在我的管道中添加了一个addtional参数

let i of someArray | groupBy:'someField':updated" 
<!--updated is updated after performing some function-->
Run Code Online (Sandbox Code Playgroud)

然后我随时对数组执行更新

updateArray(){
    //this can be a service call or add, update or delete item in the array
      .then.....put this is in the callback:

    this.updated = new Date(); //this will update the pipe forcing it to re-render.
}
Run Code Online (Sandbox Code Playgroud)

这迫使我的orderBy管道再次进行转换.而且性能要好得多.


loh*_*hul 9

只需将属性 pure 设置为 false

@Pipe({
  name: 'callback',
  pure: false
})
Run Code Online (Sandbox Code Playgroud)

  • [“_Angular 在每个组件更改检测周期中都会执行一个不纯的管道。经常调用不纯的管道,就像每次按键或鼠标移动一样。考虑到这一点,请非常小心地实现一个不纯的管道。一个昂贵的、长期的-运行管道可能会破坏用户体验。_"](https://angular.io/guide/pipes#impure-pipes) (3认同)
  • 完美的解决方案,它对我来说很有魅力! (2认同)
  • 问题的标题是“如何重新触发 Angular 2 中所有组件树上的所有纯管道”。如果你的“答案”是“让它不纯粹”,那么你就没有理解这个问题。我们开发人员都知道不纯的管道会被重新触发很多次,这不是这里的问题。 (2认同)