Angular 18 Реактивная форма ОШИБКА: к элементу управления формой с именем: 'q1124' не прикреплен экземпляр FormControl

У меня есть компонент angular, который будет динамически создавать и отображать форму с помощью ReactiveFormsModule. Когда я нажму «Отправить», он опубликует данные, и я получу новые вопросы для компонента для рендеринга и замены старых.

Первый рендеринг проходит нормально, когда вы переходите на страницу. Если я отправлю первый вопрос, родитель получит новые вопросы для передачи этому компоненту.

эта ошибка: "There is no FormControl instance attached to form control element with name: 'q1124'"

Всякий раз, когда я регистрирую form.controls, он говорит, что содержит «q1124».

форма.компонент.ts


import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges }       from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { Question } from '../../../../shared/models/views-models/question-view';
import { getQuestionAnswers } from '../../../../shared/utils/helpers.util';
import { MatButtonModule } from '@angular/material/button';
import { MatCard } from '@angular/material/card';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';

@Component({
  selector: 'app-survey-form',
  standalone: true,
  imports: [FormsModule, ReactiveFormsModule, MatFormFieldModule, MatInputModule, MatCard, MatButtonModule,],
  templateUrl: './survey-form.component.html',
  styleUrl: './survey-form.component.scss'
})
export class SurveyFormComponent implements OnChanges {
  form: FormGroup = new FormGroup({});
  @Input() questions: Question[] = []
  @Input() currentState: string | undefined;
  @Input() previousState: string | undefined;
  @Output() dataEmitter: EventEmitter<any[]> = new EventEmitter<any[]>();

  constructor(private fb: FormBuilder, private cdr: ChangeDetectorRef) { }


  ngOnChanges(changes: SimpleChanges): void {
    if (changes['questions'] && changes['questions'].currentValue !== changes['questions'].previousValue) {
      this.form = this.createForm(this.fb, this.questions)
      this.cdr.detectChanges();
    }
  }

  submit() {
    if (this.form.valid) {
      const answers = getQuestionAnswers(this.form, this.questions)
      this.dataEmitter.emit(answers)
    }
  }

  createForm = (
    formBuilder: FormBuilder,
    questions: any[]
  ): FormGroup => {
    const group: { [key: string]: AbstractControl<any, any> } = {};

    questions.forEach(question => {
      group[question.id] = question.mandatory
        ? formBuilder.control('', [Validators.required])
        : formBuilder.control('');
    });
    return new FormGroup(group)
  }
}

форма.компонент.html

<div class = "form-container">
    <form [formGroup] = "form" (ngSubmit) = "submit()" class = "input-form">
        @for (question of questions; track $index) {
        <input type = "text" matInput [formControlName] = "question.id" [required] = "question.mandatory"
            placeholder = "Type your answer here..." [id] = "question.id">
        }
        @if (currentState) {
        <button type = "submit" [disabled] = "form.invalid" mat-flat-button>Next</button>
        }
    </form>
</div>

Я пробовал разные перехватчики, такие как ngOnAfterInit, чтобы посмотреть, может ли это быть связано с каким-то временем, я также добавил ChangeDetectorRef.

ОБНОВЛЯТЬ Ссылка на Stackblitz с макетом https://stackblitz.com/edit/stackblitz-starters-4ewzry?file=src%2Fapp%2Fform-test%2Fform-test.comComponent.ts

🤔 А знаете ли вы, что...
Angular предоставляет средства для тестирования приложений, включая юнит-тестирование и интеграционное тестирование.


1
52
1

Ответ:

Решено

проблема в том, что вопросы имеют значения до создания формы

Вы можете перебирать form.controls|keyvalue

@for (control of form.controls|keyvalue; track $index) {
    <!--see that you use questions[$index].id instead of question-->
        <input type = "text" matInput [formControlName] = "questions[$index].id">
}

Но поймите, что вам нужно подождать, чтобы получить вопросы

  ngOnChanges(changes: SimpleChanges): void {
    if (
      changes['questions'] &&
      changes['questions'].currentValue !== changes['questions'].previousValue
    ) {
      //enclosed in a setTimeout without "delay"
      setTimeout(()=>{
        this.form = this.createForm(this.fb, this.questions);
      })
      this.cdr.detectChanges();
    }
  }

стекблиц