ValueChanges emite para formControls no componente, mas não formControl no componente filho?

Aug 21 2020

Eu tenho um formulário em meu aplicativo Angular que usa alguns formControls diferentes. Alguns deles estão dentro do próprio componente e alguns estão dentro de um segundo componente que é utilizado pelo primeiro.

Descobri que, se os componentes filhos alteram o valor do formulário (o que posso verificar registrando), isso não causa a emissão do observável valueChanges. Mas altera a forma, então certamente deveria causar a emissão do observável?

Eu tenho um formGroup em meu componente pai que contém diferentes formControls, um deles targetGroupsé passado diretamente para o componente filho para que ele possa manipulá-lo. Este é o que não faz com que o valueChanges observável seja acionado. No entanto, se eu registrar o formulário depois de usar o componente filho, posso ver que ele está manipulando o formulário conforme o esperado.

Componente pai

  @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);
    })
  }

Modelo principal

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

Componente filho


  @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 = "";
  }

Modelo filho

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

Respostas

3 SlawaEremin Aug 21 2020 at 17:14

você tem algum problema aqui:

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

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

neste local você alterou diretamente o valor do controle, mas o valor deve ser atualizado apenas através de setValue ou patchValue, por isso não hávalueChanges

seu código, você deve se parecer

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

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

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

em mais um aviso, é melhor usar formArray, se você tiver valor de array:

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