如何在Angular 2中强制组件的重新渲染?


Answers:


216

渲染在更改检测后发生。为了强制进行更改检测,以便将已更改的组件属性值传播到DOM(然后浏览器将在视图中呈现这些更改),以下是一些选项:

您将需要进口,然后注射ApplicationRefNgZoneChangeDetectorRef到您的组件。

对于您的特定情况,如果仅更改了一个组件,我建议使用最后一个选项。


1
最终版本的angular2的ChangeDetectorRef上是否有任何工作代码?我现在面临的情况是,在http发出创建新用户的请求后,视图未更新,然后成功将新对象推入现有的旧用户列表(用于迭代视图)。那真是很奇怪this is the first time I am facing an update not working in ng2。更改检测策略是默认的,所以我知道我还没有弄明白更改检测策略。
加里

1
@Gary,您应该发布一个新问题,并包括您的组件和服务代码(理想情况下,请包括一个演示该问题的最小插件)。我看到的一个常见问题是this在POST回调中未使用适当的上下文。
Mark Rajcok '16

您知道我执行更改时是否可以手动触发管道吗?我尝试触发更改检测,但是管道没有更新...我也尝试pure:false在管道中使用。它可以工作,但是对于我的用例来说太贵了(效率低下)。
ncohen

1
@ncohen,我不知道有任何手动触发管道更新的方法。您可以使用纯管道并在每次触发更新时更改对象引用。在“管道” 文档的“纯管道”部分中对此进行了讨论。根据您的用例,您可能想使用组件属性而不是管道。Pipes文档末尾简要讨论了该技术。
Mark Rajcok '17

1
@ N-ate,所有链接都是固定的。
Mark Rajcok

47

TX,找到我需要的解决方法:

  constructor(private zone:NgZone) {
    // enable to for time travel
    this.appStore.subscribe((state) => {
        this.zone.run(() => {
            console.log('enabled time travel');
        });
    });

运行zone.run将强制组件重新渲染


6
在这种情况下,appStore是什么-哪种变量及其类型?似乎是可观察的...但是我的可观察对象位于要单击按钮即可刷新的组件内...而且我不知道如何从父/当前位置访问子组件方法/变量
Abdeali Chandanwala

28

ChangeDetectorRef方法

import { Component, OnInit, ChangeDetectorRef } from '@angular/core';

export class MyComponent {

    constructor(private cdr: ChangeDetectorRef) { }

    selected(item: any) {
        if (item == 'Department')
            this.isDepartment = true;
        else
            this.isDepartment = false;
        this.cdr.detectChanges();
    }

}

14

我使用* ngIf强制重新加载组件。

容器中的所有组件都可以追溯到整个生命周期挂钩。

在模板中:

<ng-container *ngIf="_reload">
    components here 
</ng-container>

然后在ts文件中:

public _reload = true;

private reload() {
    setTimeout(() => this._reload = false);
    setTimeout(() => this._reload = true);
}

谢谢你,@ loonis!我觉得这应该行得通,除了,我已经拥有了一切setTimeout()。现在我的正在使用一个简单轻巧的解决方案!
LHM

如果我能
投票超过10000

需要注意的一件事-容器消失并再次出现可能会导致大小更改,并且页面可能会闪烁
ghosh

9

这里的其他答案提供了触发变更检测周期的解决方案,这些周期将更新组件的视图(与完全重新渲染不同)。

完全重新绘制,这将破坏并重新初始化组件(调用所有生命周期挂钩,并重建视图)可以通过使用来完成ng-templateng-container并且ViewContainerRef在下列方式:

<div>
  <ng-container #outlet >
  </ng-container>
</div>

<ng-template #content>
  <child></child>
</ng-template>

然后在同时引用这两个组件的组件中#outlet#content我们可以清除出口的内容并插入子组件的另一个实例:

@ViewChild("outlet", {read: ViewContainerRef}) outletRef: ViewContainerRef;
@ViewChild("content", {read: TemplateRef}) contentRef: TemplateRef<any>;

private rerender() {
    this.outletRef.clear();
    this.outletRef.createEmbeddedView(this.contentRef);
}

另外,初始内容应插入到AfterContentInit钩子上:

ngAfterContentInit() {
    this.outletRef.createEmbeddedView(this.contentRef);
}

完整的工作解决方案可以在这里https://stackblitz.com/edit/angular-component-rerender找到。


1

ChangeDetectorRef.detectChanges()通常是最专注的方法。ApplicationRef.tick()通常不是大锤的方法。

要使用ChangeDetectorRef.detectChanges(),您需要在组件顶部:

import {  ChangeDetectorRef } from '@angular/core';

...然后,通常在将其注入到构造函数中时使用如下别名:

constructor( private cdr: ChangeDetectorRef ) { ... }

然后,在适当的位置,将其命名为:

this.cdr.detectChanges();

你打电话ChangeDetectorRef.detectChanges()可以非常显著。您需要完全了解生命周期,以及您的应用程序如何正常运行以及如何呈现其组件。这里没有替代品可以完全完成您的作业并确保您了解内在的Angular生命周期。然后,一旦您了解了这一点,就可以ChangeDetectorRef.detectChanges()适当地使用它了(有时很容易理解应在何处使用它,有时它可能非常复杂)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.