如何在Angular中更新作为输入传递给子组件的嵌套FormGroup?

huangapple go评论65阅读模式
英文:

How to update a nested FormGroup passed as input to a child component in Angular?

问题

我在尝试将嵌套的 FormGroup(外部 FormGroup 的一个 FormControl)作为输入传递给子组件时遇到了问题。更明确地说,对表单组的更改会反映在其中(我可以从父组件和子组件都记录预期更改的表单组;valueChanges 订阅也有效),但变更检测从未触发。

这是最小的复现示例:

parent.component.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';

@Component({
  template: '<app-child [inputGroup]="nestedGroup"></app-child>',
})
export class AppComponent implements OnInit {
  form: FormGroup;

  constructor(private fb: FormBuilder) {}

  get nestedGroup() {
    return this.form.get('nested') as FormGroup;
  }

  ngOnInit(): void {
    this.form = this.fb.group({
      flag: [true],
    });

    this.form.setControl(
      'nested',
      this.fb.group({ field1: { key: 'testString' } })
    );

    this.form.valueChanges.subscribe((form) =>
      console.log('Form updated in parent!', form)
    );

    setTimeout(() => this.changeValue(this.nestedGroup), 5000);
  }

  changeValue(nestedForm: FormGroup) {
    console.log('Should log CHILD FORM changes now:');
    nestedForm.controls['field1'].patchValue({ key: 'newTestString' });
  }
}

child.component.ts

import { ChangeDetectorRef, Input, OnChanges } from '@angular/core';
import { SimpleChanges } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';

@Component({
  selector: 'app-child',
  template: '<p>child showing up!</p>',
})
export class ChildComponent implements OnChanges, OnInit {
  @Input()
  inputGroup: FormGroup;

  constructor(private cdr: ChangeDetectorRef) {}

  ngOnChanges(changes: SimpleChanges) {
    console.log('Form updated in child!', changes); // TOFIX: should run after 5 sec
  }

  // 下面的代码仅用于说明更改是存在的
  ngOnInit() {
    this.inputGroup.valueChanges.subscribe((value) => {
      console.log('Got valueChanges in child...', value);
      this.cdr.detectChanges();
    });
  }
}

还有一个我创建的小 StackBlitz 示例。

我尝试了显式标记表单组为脏(formGroup.markForDirty())并在两个级别上手动触发了变更检测(cdr.markForCheck(),cdr.detectChanges())。

我期望 ngOnChanges 对新输入做出反应。可能我想在子组件中操作和响应输入 FormGroup 的值。

结果是输入值Changes订阅是活动的,但输入更改不是。

英文:

I have trouble passing the nested FormGroup (the one that is a FormControl of an outer FormGroup) as an input to a child component. More precisely, the changes made to the form group are reflected in it (I can log the expected changed form group both from parent and child; valueChanges subscription works as well), but change detection never fires.

Here is the minimal reproduction:

parent.component.ts

import { Component, OnInit } from &#39;@angular/core&#39;;
import { FormBuilder, FormGroup } from &#39;@angular/forms&#39;;

@Component({
  template: &#39;&lt;app-child [inputGroup]=&quot;nestedGroup&quot;&gt;&lt;/app-child&gt;&#39;,
})
export class AppComponent implements OnInit {
  form: FormGroup;

  constructor(private fb: FormBuilder) {}

  get nestedGroup() {
    return this.form.get(&#39;nested&#39;) as FormGroup;
  }

  ngOnInit(): void {
    this.form = this.fb.group({
      flag: [true],
    });

    this.form.setControl(
      &#39;nested&#39;,
      this.fb.group({ field1: { key: &#39;testString&#39; } })
    );

    this.form.valueChanges.subscribe((form) =&gt;
      console.log(&#39;Form updated in parent!&#39;, form)
    );

    setTimeout(() =&gt; this.changeValue(this.nestedGroup), 5000);
  }

  changeValue(nestedForm: FormGroup) {
    console.log(&#39;Should log CHILD FORM changes now:&#39;);
    nestedForm.controls[&#39;field1&#39;].patchValue({ key: &#39;newTestString&#39; });
  }
}

child.component.ts

import { ChangeDetectorRef, Input, OnChanges } from &#39;@angular/core&#39;;
import { SimpleChanges } from &#39;@angular/core&#39;;
import { Component, OnInit } from &#39;@angular/core&#39;;
import { FormGroup } from &#39;@angular/forms&#39;;

@Component({
  selector: &#39;app-child&#39;,
  template: &#39;&lt;p&gt;child showing up!&lt;/p&gt;&#39;,
})
export class ChildComponent implements OnChanges, OnInit {
  @Input()
  inputGroup: FormGroup;

  constructor(private cdr: ChangeDetectorRef) {}

  ngOnChanges(changes: SimpleChanges) {
    console.log(&#39;Form updated in child!&#39;, changes); // TOFIX: should run after 5 sec
  }

  // the code below is only to illustrate that the changes are there
  ngOnInit() {
    this.inputGroup.valueChanges.subscribe((value) =&gt; {
      console.log(&#39;Got valueChanges in child...&#39;, value);
      this.cdr.detectChanges();
    });
  }
}

There is also a small stackblitz I made.

I tried explicitly marking form group as dirty (formGroup.markForDirty()) and firing change detection manually on both levels (cdr.markForCheck(), cdr.detectChanges()).

I expected ngOnChanges to react to a new input. Potentially I would like to manipulate and react to the input FormGroup values in the child.

The result was an active input valueChanges subscription, but not an input change.

答案1

得分: 0

ngOnChanges 只有在你的 inputGroup 对象实例发生更改时才会被调用。如果只是对象内部的值发生变化,这是不够的。

但正如你注意到的,你确实可以获取到更改,并订阅 valueChanges 正是正确的方式。

至于术语:变更检测实际上是在你的组件上进行的。如果在你的模板中打印表单控件的值,你应该会看到它在 patchValue 调用之后更新。这意味着Angular检测到了变化并更新了视图。但变更检测并不意味着会调用 ngOnChanges

英文:

ngOnChanges will only be called if the object instance of your inputGroup changes. If just a value inside the object changes, it is not enough.

But as you noticed, you do get the changes, and subscribing to valueChanges is exactly the way to go.

For the terminology: Change detection is actually done on your component. If you print the form control value in your template, you should see that it updates after the patchValue call. That means, Angular detected the change and updated the view. But change detection does not mean that ngOnChanges gets called.

huangapple
  • 本文由 发表于 2023年7月13日 22:59:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76680840.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定