尽管子级信号发生变化,但对父级没有影响(Angular 18)

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

在孩子中,我有一个字段

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 typescript signals effect
1个回答
0
投票

仅当实际值更改时才会触发信号,因为数组和对象作为引用存储在内存中,如果更改内部属性,内存引用不会更改,因此不会将其检测为更改。

Angular 18 中的信号和数组可变性 - Ben Nadel

Github 问题 - Angular 17 - 不支持可变信号

您可以使用

object destructuring
(对于对象)或
.slice()
数组方法(仅限数组)来创建新的内存引用,这将传播到父级。由于内存引用发生了变化,信号将把它作为新的变化来拾取。

@Component({
  selector: 'app-child',
  standalone: true,
  template: `
    <button (click)="onClick()">click</button>
  `,
})
export class ChildComponent {
  data = model({});
  onClick() {
    console.log('Click alters the data.');
    // this.data.set(this.data());
    // object destructuring creates new memory reference.
    this.data.update((a: any) => ({ ...a }));
  }
}

完整代码:

import { Component, effect, model, signal } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { interval } from 'rxjs';
@Component({
  selector: 'app-child',
  standalone: true,
  template: `
        <button (click)="onClick()">click</button>
      `,
})
export class ChildComponent {
  data = model({});
  onClick() {
    console.log('Click alters the data.');
    // this.data.set(this.data());
    this.data.update((a: any) => ({ ...a }));
  }
}

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [ChildComponent],
  template: `
    <app-child [(data)]="data" (dataChange)="dataChange($event)"/>
  `,
})
export class App {
  data = signal({});
  constructor() {
    // interval(1000).subscribe((a: any) => console.log('poll: ', this.data()));
    effect(() => {
      console.log('effect: ', this.data());
    });
  }
  ngOnInit() {
    // this.data.set(this.getData());
  }

  dataChange(e: any) {
    console.log(e);
  }
}

bootstrapApplication(App);

Stackblitz 演示

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