代码之家  ›  专栏  ›  技术社区  ›  Byron

如何在Angular中干净地检测对象的深层变化?

  •  0
  • Byron  · 技术社区  · 3 年前

    在父组件中,我有一个对象(在我的特定情况下,它是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正在做的事情(当视图引用对象上的属性时),但在我的控制器中。

    0 回复  |  直到 3 年前
        1
  •  1
  •   AT82    3 年前

    具体来说,在您的情况下,当我们处理被动形式时,我会利用 valueChanges 来处理TS中的逻辑。只需订阅子级中的valueChanges,然后执行您需要执行的任何逻辑。下面是一个非常简单的观看 difficulty 表单控件值,并根据值显示ngIf-else模板 easy 是否:

    父母亲

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

    然后将表单传递给孩子,或者传递给你用例中的任何东西,也许是嵌套的表单组?这里只是传递父窗体。

    在child中,只需听表单值的变化并将布尔值映射到 condition$ 可观察的, true false 取决于 困难 价值

    @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>
    

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

    STACKBLITZ

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

    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')
    );
    

    STACKBLITZ