Angular 11 — проверка дочерней формы, созданной в цикле for

Я использую формы Angular Reactive. У моего родительского компонента есть форма Add Form. кнопки Submit и Reset. Когда я нажимаю на Add form, он добавляет дочернюю форму с именем profile-form в DOM. profile-form имеет два поля First Name и Email. Идея состоит в том, чтобы повторно использовать компонент profile-form любое количество раз при нажатии кнопки добавления. Каждая форма должна быть проверена, и родительская форма должна знать статус проверки каждой дочерней формы.

В моей родительской форме есть этот HTML-код для создания дочерних форм в цикле for.

<div *ngFor="let fg of formList.controls; let infoIndex = index">
    <app-profile-form formControlName="fg" [formList]="formList" 
                      [formIndex]="infoIndex"></app-profile-form>
  </div>

Когда я смотрю на консоль, я получаю сообщение об ошибке, что formControlName 'fg' не найдено в formList.controls. Как исправить это сопоставление formControls между моей родительской и дочерней формами, чтобы проверка работала?

Stackblitz здесь


person Jake    schedule 01.05.2021    source источник


Ответы (1)


Попробуем проанализировать форму.

Вы ожидаете ниже как конечное значение формы

{
  "formList": [  
    {
      "firstName": "",
      "email": ""
    },
    {
      "firstName": "",
      "email": ""
    },
    {
      "firstName": "",
      "email": ""
    }
  ]
}

В приведенном выше мы имеем

form  => FormGroup : form 
  formList => FormArray : formList 
    1 => FormControl with value {email: '', firstName} : 1
    2 => FormControl with value {email: '', firstName} : 2
    3 => FormControl with value {email: '', firstName} : 3

Итак, в форме у нас должна быть эта структура, чтобы форма работала.

<form [formGroup]="signupForm" (ngSubmit)="submit()">
  <ng-container formArrayName='formList'>
    <div *ngFor="let fg of formList.controls; let infoIndex = index">
      <app-profile-form [formControlName]="infoIndex" [formIndex]="infoIndex"></app-profile-form>
    </div>
  </ng-container>

  <button>Sign Up</button>
  <button type="button" (click)="resetForm()">Reset</button>
</form>

Некоторые другие поправки будут включать

Изменение formListGroupDef

    return this.formBuilder.control(
      {
        firstName: "",
        email: ""
      },
      Validators.required
    );

SignUpForm

    this.signupForm = this.formBuilder.group({
      formList: this.formBuilder.array([
        this.formListGroupDef()
      ])
    });

Я также внес несколько изменений в ваш ProfileFormComponent

export class ProfileFormComponent implements ControlValueAccessor, OnDestroy {
  @Input() formIndex: any;
  destroyed$ = new Subject<any>();
  form: FormGroup;
  get firstNameControl() {
    return this.form.controls.firstName;
  }

  get emailControl() {
    return this.form.controls.email;
  }

  constructor(private formBuilder: FormBuilder) {
    this.form = this.formBuilder.group({
      firstName: ["", Validators.required],
      email: ["", Validators.required]
    });

    this.form.valueChanges.pipe(
      filter(({firstName, email}) => firstName.length > 0 || email.length > 0 ),
      takeUntil(this.destroyed$)
    ).subscribe(value => {
      this.onChange(value);
      this.onTouched();
    });
  }

  ngOnDestroy() {
    this.destroyed$.next();
  }

  onChange: any = () => {};
  onTouched: any = () => {};

  registerOnChange(fn) {
    this.onChange = fn;
  }

  writeValue(value) {
    if (value) {
      this.form.patchValue(value);
    }
  }

  registerOnTouched(fn) {
    this.onTouched = fn;
  }

  validate(_: FormControl) {
    return this.form.valid ? null : { profile: { valid: false } };
  }

Посмотреть демо

person Owen Kelvin    schedule 01.05.2021
comment
Это работает отлично. Спасибо, Оуэн! - person Jake; 01.05.2021