为什么 ngOnChanges() 在 @Input() 更新数据 Angular 8 时不触发?

问题描述 投票:0回答:6

parent.component.html

<parent-comp [data]="mydata"> </parent-comp>

parent.component.ts

this.service.abc$
        .takeUntil(this.ngUnsubscribe.asObservable())
        .subscribe((data: myType[]) => {
            this.mydata= data;
        });

child.component.ts

@Input data;

下课我有下面的代码

public ngOnChanges() { if (this.data) { console.log(this.data); } }

现在我希望每当我从父组件到子组件的

@Input data
中收到最新数据时,我的
ngOnChanges
函数应该触发并在控制台中打印数据。

但不幸的是

ngOnChanges
功能没有再次触发。它只在组件初始化时触发一次

如果有人想要更多详细信息,请告诉我!

谢谢!

javascript angular typescript ecmascript-6 lifecycle
6个回答
4
投票

鉴于缺乏进一步的信息,我会做出一个有根据的猜测

@Input data
要么是一个数组要么是一个对象。

根据docs

ngOnChanges
是:

一个生命周期钩子,当一个对象的任何数据绑定属性被调用时 指令更改。

但是它没有说的是how属性应该改变。一般来说,钩子只有在对属性的引用发生变化时才会触发。

考虑以下例子

父组件控制器

mydata = [];

updateMyData(value: any) {
  this.mydata.push(value);
}

父组件模板

<app-child [data]="mydata"></app-child>

子组件控制器

@Input() data: any;

ngOnChanges(changes: SimpleChanges) {
  console.log(changes);
}

现在您会期望每次在父组件中调用

ngOnChanges
函数时都会触发
updateMyData()
。但是对变量
mydata
的引用永远不会改变。所以钩子不会被触发。有多种方法可以强制更改检测器触发挂钩。

方法一:

@Input
装饰器绑定到 setter 而不是直接绑定到变量。已经在answer中讨论过。

方法二: 使用扩展语法重新分配父组件中的变量。

父组件控制器

mydata = [];

updateMyData(value: any) {
  this.mydata = [...this.mydata, value];
}

您也可以对对象使用相同的方法。


2
投票

NgOnChanges 只会在原始类型的输入绑定属性更改时触发。这是因为必须更改对数据变量的引用才能注册更改,以便您可以在此生命周期挂钩中获取它。

因此,实现此目的的可能方法是更改“mydata”变量的引用。比如,当 mydata 变量发生变化时,为它分配一个新的引用,

mydata = [...mydata]
如果它是一个数组,或者
mydata = {...mydata}
如果它是来自父组件的对象。


2
投票

感谢大家提供快速有效的解决方案。

我得到了解决方案,它与你们的建议不完全相同。

在父组件中:

**Earlier I was assigning this way**
`this.mydata= data;`


**But now I am assigning in below way:**
`this.mydata= cloneDeep(data);`

注意:

cloneDeep
是从
lodash

进口的

1
投票

可能是你传递的数据。如果它没有改变,那么 ngOnChanges 将不会注册任何更改。这是一个示例,您可以看到一个属性是否多次更改,然后它只会在第一次更新时触发,但如果您重新创建对象,它每次都会更改。 (查看 stackblitz 中的控制台日志)

https://stackblitz.com/edit/angular-ivy-qmb35h?file=src%2Fapp%2Fapp.component.html

你可以像我一样做,每次都重新创建对象来绕过这个,或者更 hacky 的方法可能是保留一个你传递的虚拟“计数”变量,并在每次你想要子组件时递增它注册更改。


0
投票

您可以使用 setter 和 getter 方法为 @Input 设置角度。 请参考以下几行以供参考:

private _data: any;
  @Input()
  set data(data) {
    this._data = data;
    console.log(this._data);
  };

仅从 setter 方法,您也可以调用任何其他方法,并且可以从那里运行您想要的任何逻辑。


0
投票

您还可以在将对象传递给子对象之前在父对象中对对象进行 JSON 字符串化,然后 JSON 将字符串解析为子对象中的原始对象。

您甚至不必解析子项中的字符串。只需将父对象中的对象字符串化,将它与对象一起传递给父 HTML 模板,不要为子对象中的字符串创建

@Input()
ngOnChanges
会开火,你可以用传递的对象做一些事情。

我并不是说这是最好的解决方案,只是说这是一个解决方案。使用传播运算符是我使用的解决方案。

© www.soinside.com 2019 - 2024. All rights reserved.