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

Posted

技术标签:

【中文标题】如何在 Angular 2 中强制组件重新渲染?【英文标题】:How to force a component's re-rendering in Angular 2? 【发布时间】:2016-05-08 10:03:35 【问题描述】:

出于调试目的,我想强制一个组件重新渲染它的视图,这可能吗?

【问题讨论】:

“重新渲染”是什么意思。更新绑定? 只是一个简单的问题,为什么需要强制重新渲染? Triggering Angular2 change detection manually的可能重复 【参考方案1】:

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

ApplicationRef.tick() - 类似于 Angular 1 的 $rootScope.$digest() - 即检查完整的组件树 NgZone.run(callback) - 类似于 $rootScope.$apply(callback) - 即评估 Angular 2 区域内的回调函数。我认为,但我不确定,这最终会在执行回调函数后检查完整的组件树。 ChangeDetectorRef.detectChanges() - 类似于 $scope.$digest() - 即仅检查此组件及其子组件

您需要导入然后将ApplicationRefNgZoneChangeDetectorRef 注入到您的组件中。

对于您的特定场景,如果只有一个组件发生了更改,我建议使用最后一个选项。

【讨论】:

关于 angular2 最终版本的 ChangeDetectorRef 上的任何工作代码?我现在面临的情况是,在 http 的 post 请求创建新用户之后视图没有更新,然后成功将新对象推送到现有的旧用户列表(用于在视图中迭代)。很奇怪this is the first time I am facing an update not working in ng2。更改检测策略是默认的,所以我知道我没有搞砸更改检测策略。 @Gary,您应该发布一个新问题并包括您的组件和服务代码(理想情况下,包括一个演示问题的最小 plunker)。我看到的一个常见问题是在 POST 回调中没有使用正确的 this 上下文。 您知道我是否可以在执行更改时手动触发管道?我试图触发更改检测,但管道没有更新......我也尝试在管道中使用pure:false。它可以工作,但对于我的用例来说太昂贵(效率低​​下)。 @ncohen,我不知道有什么方法可以手动触发管道更新。您可以使用纯管道并在想要触发更新时更改对象引用。这在Pipes doc 的“纯管道”部分下进行了讨论。根据您的用例,您可能希望使用组件属性而不是管道。 Pipes 文档末尾简要讨论了这种技术。 @N-ate,所有链接都已修复。【参考方案2】:

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

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

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

【讨论】:

什么是 appStore 在这种情况下 - 哪种变量及其类型?似乎是可观察的……但我的可观察对象位于我想通过单击按钮刷新的组件内……而且我不知道如何从父/当前位置访问子组件方法/变量【参考方案3】:

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


【讨论】:

【参考方案4】:

我使用 *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(),我什么都有。现在我正在使用一个简单而轻量级的解决方案! 需要注意的一件事 - 容器消失和再次出现可能会导致大小更改并且页面可能会闪烁 这对我来说是最好的解决方案。这使用我的自定义翻译管道解决了我的翻译问题。【参考方案5】:

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

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

<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。

【讨论】:

【参考方案6】:

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

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

import   ChangeDetectorRef  from '@angular/core';

...然后,通常当您将其注入构造函数时,您会像这样:

constructor( private cdr: ChangeDetectorRef ) ...

然后,在适当的地方,你可以这样称呼它:

this.cdr.detectChanges();

在哪里您调用ChangeDetectorRef.detectChanges() 可能非常重要。您需要完全了解生命周期以及您的应用程序是如何运行和呈现其组件的。这里没有什么可以替代完全做你的功课并确保你完全理解 Angular 生命周期。然后,一旦你理解了这一点,你就可以适当地使用ChangeDetectorRef.detectChanges()(有时很容易理解你应该在哪里使用它,有时它可能非常复杂)。

【讨论】:

以上是关于如何在 Angular 2 中强制组件重新渲染?的主要内容,如果未能解决你的问题,请参考以下文章

强制 Vue 组件重新渲染

Vue组件强制刷新的解决方案

如何从另一个组件重新渲染特定组件模板?角 2

如何在 React 中强制渲染组件

如果子组件发生更改,如何重新渲染父组件

变异后如何强制App重新渲染?