在子级中,我有一个字段data
,单击时我会更改其内容。然后,我希望在父级中触发一个效果来检测所述更改。出于某种原因,当我更改子级中的模型输入时,该效果不会触发(但是,在父级中设置信号时会触发该效果)。
孩子有这个。
export class ChildComponent {
data = model({} as Vm);
onClick() {
console.log("Click alters the data.");
// this.data.set(this.data());
this.data.update(a => a);
}
}
父母有这个。
export class ParentComponent {
constructor() {
interval(1000).subscribe(a => console.log("poll: ", this.data()));
effect(() => { console.log("effect: ", this.data()) });
}
data = signal({} as Vm);
ngOnInit() { this.data.set(this.getData()); }
}
我看到初始效果显示一个空对象。然后,我看到父级的 init 方法的更新。我还看到第二次检查显示子级中更改的值。然而 - 该效果不会再次触发,只会触发上述两次。
我不知道如何进一步排除故障。查看官方文档后,我发现它应该可以工作(它确实可以工作!)但我完全不确定我是否完全正确地在子组件中执行了更改。
(在实际情况下,我只会改变作为模型输入一部分的数组中对象中的字段,data
但这是下一步。首先,我想了解为什么执行set(...)
或update(...)
触发父项中的效果。
仅当实际值发生变化时才会触发信号,因为数组和对象在内存中存储为引用,如果您更改内部属性,内存引用不会改变,因此不会将其检测为更改。当您按原样返回对象时,它也不会改变。
Angular 18 中的信号和数组可变性 - Ben Nadel
Github 问题 - Angular 17 - 不支持可变信号
您可以使用
object destructuring
(针对对象)或.slice()
数组方法(仅限数组)创建新的内存引用,并将此引用传播到父级。由于内存引用已更改,因此它将被信号拾取为新的更改。此解释仅适用于
effect
和,computed
因为它们只对信号变化做出反应。有时您可能会注意到包含对象的信号的内部属性的 HTML 更新正常(例如
{{ data()?.qwerty?.test }}
:)。这不是由于信号,而是由于正在运行变更检测(单击事件,运行 cdr.detectChanges()),Angular 将触发变更检测周期,并更新信号内嵌套属性的最新值。这并不意味着信号支持可变对象。它只是变更检测运行的副作用。完整代码:
Stackblitz 演示