首页 > 解决方案 > 如何干净地检测Angular中对象的深层变化?

问题描述

在父组件中,我有一个对象(在我的特定情况下,它是 FormBuilder 的 FormGroup 的控件),我将其作为输入传递给子组件。

<my-component form="fb.controls"></my-component>

在子组件中,我定义了输入:

@Input()
form: any;

如果我的子组件的模板引用此对象的属性,则更改检测是自动的。例如:

<div>{{form.myField}}</div>

但是,在某些情况下,我需要处理输入值,然后将其他内容放入模板中。我想避免将逻辑嵌入到模板中。举个简单的例子,这是可行的,但并不漂亮:

<div [class]="form ? form.difficulty ? form.difficulty.value == 'easy' ? 'green' : form.difficulty.value == 'hard' ? 'red' : 'orange' : 'brown' : 'black'"></div>

我发现的替代品都有问题。

  1. 在模板中调用方法意味着它将被调用无数次
  2. 向父组件添加任何代码,例如事件发射器或 ngModelChanges,似乎违反了关注点分离,并且比应有的更复杂
  3. 当只有对象上的一个属性发生变化时,不会调用 ngOnChanges

我希望有一种简单(模板中的代码最少)的方法可以让 Angular 处理更改检测,就像我在模板中显式引用属性一样顺利。

编辑:理想的答案不取决于提供可观察的对象(例如 valueChanges)。Angular 在进行神奇的更改检测时不需要这样做。我想准确地重现 Angular 正在做什么(当视图引用对象上的属性时),但在我的控制器中。

标签: angular

解决方案


具体来说,在您的情况下,当我们处理反应形式时,我将利用valueChanges它来处理 TS 中的逻辑。只需订阅孩子的 valueChanges 并执行您需要执行的任何逻辑。这是一个非常简单的示例,它查看difficulty表单控件的值并根据值是否存在显示 ngIf - else 模板easy

家长:

form: FormGroup;

constructor(private fb: FormBuilder) {
  this.form = this.fb.group({
    difficulty: ['']
  })
}

然后将表单传递给孩子或您的用例中的任何内容,也许是嵌套的表单组?这里只是传递父表单。

在 child 中,只需监听表单值的变化并将布尔值映射到condition$observable,true或者false取决于difficulty值:

@Input() form;
condition$: Observable<boolean>;

ngOnInit() {
  this.condition$ = this.form.valueChanges.pipe(
    // using 'any' just for the sake of the demo... do NOT use any!
    map((value: any) => value.difficulty === 'easy')
  );
}

那么模板就是:

<div *ngIf="condition$ | async; else elseTemplate">
  <p style="color: green">Difficulty is easy!</p>
</div>

<ng-template #elseTemplate>
  <p style="color: red">Difficulty is not set as "easy"</p>
</ng-template>

正如一开始已经提到的,这是一个非常简单的例子,但它应该展示我建议你可以做的事情。

堆栈闪电战

如果您将嵌套的表单组传递给子级并且实际上想要监听父级的更改,那么也可以实现,只需parent在 valueChanges 之前添加:

this.condition$ = this.form.parent.valueChanges.pipe(
  // using 'any' just for the sake of the demo... do NOT use any!
  map((value: any) => value.difficulty === 'easy')
);

堆栈闪电战


推荐阅读