在 child 组件属性中更改 @Input() 会阻止 parent 组件的进一步更新

Changing @Input() in child compoent properties prevents further updates from parent component

我有 2 个组件 - Parent 和 Child。请参阅 stackblitz demo Parent 的模板如下所示 -

单击 'Show Child' 时,isChild1Visible 设置为 true,导致 Child 组件显示其模板。

当您单击 'Hide me' 时,Child 组件将 isVisible 设置为 false,使其隐藏。现在,当再次单击 'Show Child' 时,不会显示 child 组件。

我在 Child 组件中添加 ngOnChanges() 来查看变化检测,但是我看到了下面的内容 child组件初始化时:

第一次点击'Show me'后

单击 'Hide me' 后,即使稍后单击 'Show me',ngOnChanges() 也不会打印任何内容。

那么,为什么在 child 组件中更新输入 属性 后更改检测停止工作?

问题

第一次设置 isChild1Visible 时,通过单向绑定它还会将 child isVisible 设置为 true。

从内部,您将 isVisible 设置为 false,因此 div 消失,因为您有 *ngIf="isVisible"

不过,外层的isVisible还是true!

当您再次点击外部按钮时,由于值保持不变,因此没有检测到任何变化,因此没有任何内容传递给 child。

看到这个 stackblitz demo 两个布尔值都暴露了。

选项 1:明确监听输出

如果你想从外部控制一个组件,你应该把逻辑放在外面。

一种可能的方法是在您的 child 上添加一个 @Output,这是一个 EventEmitter,当您想要关闭 child.

parent 将侦听该事件并执行某些操作,例如将 isVisible 设置为 false。

你可以在this updated stackblitz上看到它。

选项 2:Two-way 绑定

类似于解决方案选项 1,您可以创建一个与您的输入同名的发射器,以 Change 结尾并在那里触发您的更改。

在这种情况下,您只需将绑定替换为 [(isVisible)]="isChild1Visible" 并将 isVisibleChange 添加为 @Output EventEmitter<any>

演示 stackblitz

由于数据 isChild1Visible 是作为基本类型(布尔值)传递的,它将“按值传递”。

因此,如果传递对象、数组等,则为引用传递,而对于数字等原始类型,则为值传递。

在不添加任何额外输出的情况下尝试此操作:

MyAppChild1Component

export class MyAppChild1Component implements OnInit {
  @Input() isVisible;
  constructor() {}

  ngOnInit() {}
  onClick() {
    this.isVisible.value = false;
  }
}

<div *ngIf="isVisible.value">
  <span>
Child
</span>
  <button (click)="onClick()">Hide me</button>
</div>

AppComponent

export class AppComponent {
  isChild1Visible = { value: false };
  ngOnInit() {}
  onShowChild() {
    this.isChild1Visible.value = true;
  }
}

<div>
  <p>Parent</p>
  <button (click)="onShowChild()">show Child</button>
  <app-my-app-child1 [isVisible]="isChild1Visible"></app-my-app-child1>
</div>

发生这种情况是因为 app.component 中的 isChild1Visible 在子组件设置时未更新 isVisible.

首先点击onShowChild()

  • isChild1Visible = 真
  • isVisible = 真

第二次点击子 onClick()

  • isVisible = false;
  • isChild1Visible = 真

第二次点击 onShowChild()

  • isChild1Visible = true // => 这里没有变化
  • isVisible = 假

由于 isChild1Visible 的值未更改,因此 Angular 不会更新任何内容。

为了触发变更检测并保持值同步,您需要使用 Two-Way binding

<app-my-app-child1 [(isVisible)]="isChild1Visible"></app-my-app-child1>
export class MyAppChild1Component implements OnInit {
  @Input() isVisible;
  @Output() isVisibleChange = new EventEmitter<boolean>();

  constructor() {}

  ngOnInit() {}
  onClick() {
    this.isVisibleChange.emit(!this.isVisibleChange);
  }
}

Updated Sandbox