ValueChanges испускает для formControls в компоненте, но не для formControl в дочернем компоненте?

Aug 21 2020

У меня есть форма в моем приложении Angular, которая использует несколько разных formControls. Некоторые из них находятся внутри самого компонента, а некоторые - во втором компоненте, который используется первым.

Я обнаружил, что если дочерние компоненты изменяют значение формы (что я могу проверить путем записи в журнал), это не вызывает испускания наблюдаемого valueChanges. Но он меняет форму, так что, конечно же, он должен вызывать излучение наблюдаемого?

У меня есть formGroup в моем родительском компоненте, который содержит разные formControls, один из них targetGroupsпередается непосредственно дочернему компоненту, чтобы он мог управлять им. Это тот, который не вызывает срабатывание наблюдаемого valueChanges. Однако, если я регистрирую форму после использования дочернего компонента, я вижу, что он манипулирует формой, как и ожидалось.

Родительский компонент

  @Input() alertData: SafeguardAlert;

  form: FormGroup;

  constructor(private formBuilder: FormBuilder) { }

  ngOnInit() {
    this.form = this.formBuilder.group({
      enabled: this.formBuilder.control(this.alertData.enabled),
      frequency: this.formBuilder.control(this.alertData.frequency),
      targetGroups: this.formBuilder.control(this.alertData.targetGroups),
    });

    this.formChange();
  }

  get targetGroups(){
    return this.form.get('targetGroups') as FormControl;
  }

  //TODO :: Temp, remove later
  formChange(){
    this.form.valueChanges.subscribe(res => {
      console.log(res);
    })
  }

Родительский шаблон

<div>
  <form [formGroup]="form">
    <mat-expansion-panel>
      <div class="alert-body">
        <div class="width-33 mt-md">
          <mat-form-field>
            <mat-label>
              Alert Frequency
            </mat-label>
            <mat-select [formControlName]="'frequency'">
              <mat-option [value]="'instant'">
                Instant
              </mat-option>
              <mat-option [value]="'daily'">
                Daily
              </mat-option>
            </mat-select>
          </mat-form-field>
        </div>
          <app-alert-chip-list [fControl]="targetGroups"></app-alert-chip-list>
      </div>
    </mat-expansion-panel>
  </form>
</div>

Дочерний компонент


  @Input() fControl: FormControl;

  constructor() { }

  ngOnInit() {
  }

  pushToFormcontrol(value: any) {
    this.formControlArray.push(value);
  }

  get formControlArray(){
    return this.fControl.value as string[];
  }

  spliceFromFormcontrol(index: number){
    this.formControlArray.splice(index, 1);
  }

  clearInput(ref: HTMLInputElement){
    ref.value = "";
  }

Дочерний шаблон

<div class="card chip-list-padding p-sm">
  <div class="flex-row align-items-center">
    <mat-icon class="ml-sm mr-sm">person</mat-icon>
    <mat-chip-list class="flex-row flex-wrap">
      <div class="mr-sm" *ngFor="let element of formControlArray, let i = index">
        <mat-chip [disableRipple]="true" class="chip" >
          <div>
            <span>{{element}}</span>
            <button type="button" mat-icon-button [disableRipple]="true" (click)="spliceFromFormcontrol(i)">
              <mat-icon>clear</mat-icon>
            </button>
          </div>
        </mat-chip>
      </div>
      <div class="chip-list-input-cont">
        <input #listInput class="chip-list-input"
               (keyup.enter)="pushToFormcontrol(listInput.value); clearInput(listInput);">
      </div>
    </mat-chip-list>
  </div>
</div>

Ответы

3 SlawaEremin Aug 21 2020 at 17:14

у вас тут проблема:

  pushToFormcontrol(value: any) {
    this.formControlArray.push(value);
  }

  get formControlArray(){
    return this.fControl.value as string[];
  }

в этом месте вы напрямую изменили значение элемента управления, но значение должно обновляться только через setValue или patchValue, поэтому нет valueChanges

ваш код должен выглядеть как

  pushToFormcontrol(value: any) {
    this.fControl.patchValue([...formControlArray, value]);
  }

  get formControlArray(){
    return this.fControl.value as string[];
  }

  spliceFromFormcontrol(index: number){
    this.formControlArray.splice(index, 1);
  }

Еще одно замечание, лучше использовать formArray, если у вас есть значение массива:

 this.form = this.formBuilder.group({
      enabled: this.formBuilder.control(this.alertData.enabled),
      frequency: this.formBuilder.control(this.alertData.frequency),
      targetGroups: this.formBuilder.array([]),
 });