Angular @Input getter/setter 和非原始值 [英] Angular @Input getter/setter and non-primitive values

查看:22
本文介绍了Angular @Input getter/setter 和非原始值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题:我希望能够在每次子组件绑定到的对象中的属性发生更改时调用一个函数.然而,setter 只被调用一次,即使可以明显看到绑定的输入属性正在更新.

The Problem: I want be able to call a function each time a property in the object the child component is bound to changes. However, the setter is only called once, even though the bound input property can visibly be seen updating.

这一切都是因为需要将子组件绑定到其父组件属性,而父组件属性恰好是一个具有深度嵌套属性的复杂对象.我了解到当对象中的嵌套属性更改时,Angular onChange 事件不会触发.因此决定改用 getter/setter.但是,正如这个问题所见,使用 getter/setter 也不起作用.我已经更改了我的子组件以订阅父组件订阅的同一个 Observable,从而直接从服务接收更新并绕过父组件.我已经对 Angulars 绑定和 TypeScript getter/setter 进行了大量研究,并且从各方面来看,它看起来像我的代码显示工作,但事实并非如此.

This all came to be from the need to have a child component bind to its parent components property that happens to be a complex object with deeply nested properties. I've learned that the Angular onChange event does not fire when a nested property in an object changes. Hence the decision to use getters/setters instead. However, as seen by this question using getters/setters did not work either. I've since changed my child component to subscribe to the same Observable that the parent component is subscribed to, thereby receiving the updates directly from the service and bypassing the parent component all together. I've done a lot of research on Angulars binding and TypeScript getters/setters and by all accounts it looks like my code show work, but it does not.

目标: 了解为什么通过使用带有 getter/setter 的 @Input 绑定到子组件中的父组件属性对于非原始类型无法按预期工作.我是否遗漏了一个基本概念,或者我的代码中是否存在实现错误?

Goal: Understand why binding to a parent components property in a child component by using @Input with a getter/setter does not work as expected for non-primative types. Is there a fundamental concept I am missing or is there an implementation error in my code?

我将在此处展示一些源代码,并附上 StackBlitz 供任何想要现场观看的人使用.StackBlitz 现场演示

I will show some source code here and also attach a StackBlitz for anyone who wants to see it live in action. StackBlitz Live Demo

@Injectable()
export class MockDataService {
  public updateSubject: Subject<any> = new Subject();
  public numObj = {
    'prop1': 'stuff',
    'prop2': 'stuff',
    'prop3': 'stuff',
    'prop4': 'stuff',
    'level1': {
      'level2': {
        'target': 0 //target is the prop that will be getting updated
      }
    }
  }
  constructor() {
    this.startDemo();
  }
  private startDemo(): void {
    //This is simulating the server sending updates
    //to the numObj
    setInterval(() => {
      this.numObj.level1.level2.target += 1;
      this.update();
    }, 4000);
  }
  private update(): void {
    try {
      this.updateSubject.next(this.numObj);
    } catch (err) {
      this.updateSubject.error(err);
    }
  }
}

app.component.ts(父 cmp)

app.component.html<child-cmp [targetNumber]="targetNumber"></child-cmp>

export class AppComponent implements OnInit {
  public targetNumber: any;
  public displayCurrentNumber: number;
  constructor(private mockDataService: MockDataService){}
  ngOnInit(){
    this.mockDataService.updateSubject.subscribe({
      next:(data) => this.onUpdate(data),
      error: (error) => alert(error),
    });
  }
  private onUpdate(data: any): void{
    if(data){
      this.targetNumber = data;
      this.displayCurrentNumber = data.level1.level2.target;
    }
  }
}

child-cmp.component.ts

export class ChildCmpComponent {
  private _targetNum: any;
  public displayNumberObj: any;
  public displayNumber: number;
  public changeArray: string[] = [];
  @Input() 
  set targetNumber(target: any){
    this.changeArray.push('Setter(),');
    this._targetNum = target;
    this.setDisplay(this._targetNum);
  }
  get targetNumber(): any{
    this.changeArray.push('Getter(),');
    return this._targetNum;
  }
  private setDisplay(target: any): void{
    this.changeArray.push('setDisplay(),');
    this.displayNumberObj = target;
    this.displayNumber = target.level1.level2.target;
  }
}

推荐答案

这有两个部分:

  1. 认识到 @Input 装饰器仅在变更检测期间更新,因此分配给绑定数据的 setter 只会在变更检测期间触发.这个事实在 Angular 源代码的前两行注释中清楚地说明了.
  1. Recognizing that the @Input decorator is only updated during change detection, therefore the setter assigned to the bound data will only fire during change detection. This fact is clearly stated in the first two comment lines in the Angular source code.

导出接口 InputDecorator {/*** 声明数据绑定输入属性.** Angular 在变更检测期间自动更新数据绑定属性.*

  1. 从 1 开始,我们需要了解 Angulars 更改检测如何应用于非原始对象.

为了帮助解释这一点,我将使用以下对象 ObjA:

To help explain this I will use the following object ObjA:

public ObjA = {
    'prop1': 'value1',
    'prop2': 'value2'
  }

Angulars 更改检测在数据绑定属性的值更改时触发.但是,当绑定到的属性是像 ObjA 这样的对象时,绑定到的是 ObjA 的引用,而不是对象本身.正是由于这个原因,当 ObjA 中的属性值发生变化(状态变化)时,Angulars 变化检测不会触发.Angular 不知道 ObjA 的状态,而是知道对 ObjA 的引用.

Angulars change detection fires when the value of the data bound property changes. However, when the property being bound to is an object like ObjA, it is a reference of ObjA that gets bound to, not the object itself. It is for this reason when a property value in ObjA changes ( a state change) Angulars change detection does not fire. Angular is not aware of the state of ObjA, but rather the reference to ObjA.

感谢@JBNizet 和@Jota.Toledo 为我提供了理解该主题所需的信息(在上述评论中).

Thank you to @JBNizet and @Jota.Toledo for providing me the information (in the above comments) I needed to understand this topic.

这篇关于Angular @Input getter/setter 和非原始值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆