【问题标题】:Angular view not updated following changes to OnPush Component created through ViewContainerRef更改通过 ViewContainerRef 创建的 OnPush 组件后,角度视图未更新
【发布时间】:2018-02-08 15:02:42
【问题描述】:

使用 Angular 4.3 和以下Plunkr

请考虑以下组件:

@Component({
  selector: 'my-app',
  template: `
    <div>
      <button type="button" (click)="toggle()">Toggle</button>
      <div #anchor></div>
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class App {
  @ViewChild('anchor', {read: ViewContainerRef}) anchor: ViewContainerRef;
  dynamicRef: ComponentRef;
  value = true;

  constructor(private cfr: ComponentFactoryResolver, private cdr: ChangeDetectorRef) {}

  ngAfterViewInit(): void {
    let factory = this.cfr.resolveComponentFactory(Dynamic);
    this.dynamicRef = this.anchor.createComponent(factory);
    this.dynamicRef.instance.value = this.value;
    this.dynamicRef.changeDetectorRef.detectChanges();
  }

  toggle(): void {
    this.value = !this.value;
    this.dynamicRef.instance.value = this.value;
    this.dynamicRef.changeDetectorRef.detectChanges();
  }
}

@Component({
  template: `Value: {{value}}`,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class Dynamic {
  @Input() value = true;
}

App 组件使用标准ComponentFactoryResolver + ViewContainerRef 策略创建Dynamic 组件。这两个组件都有一个OnPush 更改检测策略。当调用App.toggle() 方法时,它会切换App.value,将这个新值传播到Dynamic.value,并强制在动态组件的更改检测器上运行更改检测。我希望动态组件的模板显示正确的值,但它实际上永远不会改变。将这两个组件切换到 Default 更改检测策略可提供预期的行为。

为什么动态组件模板没有正确重新渲染,如何解决?

【问题讨论】:

  • 可能不相关:App 类缺少 AfterViewInit 实现
  • 不确定你的意思,我确实在 Plunkr 和这篇文章中看到了 App.ngAfterViewInit() {...}?
  • 导出类App实现AfterViewInit angular.io/api/core/AfterViewInit#how-to-use
  • 是的,生命周期接口尚未添加到类声明中。我认为这与这个问题无关。
  • 我就是这么说的:)

标签: angular


【解决方案1】:

每个动态创建的组件都有一个宿主视图。所以你有以下层次结构:

AppComponentView
   DynamicComponentHostView
       DynamicComponentView

当你这样做时:

this.dynamicRef.changeDetectorRef

你会得到changeDetectorRef 中的DynamicComponentHostView。当您运行 detectChanges 时,您会运行 DynamicComponentHostView 的更改检测,而不是 DynamicComponentView

然后,如果您设置OnPush 策略,则父组件会更新子组件的绑定并决定是否对子组件运行更改检测。但是,重要的是,在模板中编译期间定义输入绑定。动态组件并非如此。因此,如果要使用 OnPush,则必须在此组件上手动触发更改检测。为此,您需要获取DynamicComponentView 的变化检测器。你可以这样做:

export class Dynamic {
  @Input() value = true;

  constructor(public cd: ChangeDetectorRef) {

  }

然后像这样触发变化检测:

this.dynamicRef.instance.cd.detectChanges();

Here is the plunker.

有关更改检测的更多信息,请阅读Everything you need to know about change detection in Angular

【讨论】:

  • @Spiff,不客气。我在angularindepth.com 中写了很多关于这类东西的文章。您可能想关注该出版物。祝你好运
  • 绝对有帮助
  • 感谢您的精彩回答!是否可以在不在构造函数中声明的情况下获取动态创建的组件的 ChangeDetectorRef ?喜欢从组件的可注入服务列表中获取它?
  • @mvermand,我不这么认为
猜你喜欢
  • 2017-08-14
  • 2018-04-02
  • 1970-01-01
  • 2018-03-01
  • 2019-01-19
  • 2017-05-26
  • 2020-12-17
  • 2016-11-19
相关资源
最近更新 更多