编辑:这是我的Stackblitz应用程序的Editor url,它以简化的设置显示了第一个控件和第二个控件之间的值"转移"问题.

我正在处理一个Angular 动态的表单,其中表单 struct 会根据用户输入进行更改.表单中的每个问题都由FormGroup中的唯一控件表示,这些控件在用户浏览问题时动态创建或更新.我的方法包括更新CurrentControlName并在导航时重新创建表单控件(使用nextPage和PrevessPage方法),以确保每个问题在表单中都有自己的唯一控件.

方法:

我使用服务来获取基于所选服务的问题. 表单在ngOnInit中初始化,第一个问题作为当前问题. 在问题之间导航时,我 for each 问题创建一个新的FormControl,使用问题ID通过唯一的名称进行标识. 我将响应存储在一个对象中,以便即使在问题之间来回导航时也能维护答案.

问题:

在输入第一个问题的回答并导航到第二个问题后,第二个问题的输入字段错误地显示为第一个问题输入的值.此外,更新第二个问题的值不会触发预期的valueChanges observable,而且表单控件和模板之间似乎存在不匹配或绑定问题.

具体地说,就是:

第一个输入控件按预期工作,记录值更改. 在导航到第二个问题并更新其控件之后,没有记录任何更改,这表明可能没有为新创建的控件正确设置Observable. 尽管在每个导航上重新创建了表单组和控件,但模板中控件值的绑定似乎一直存在问题.

try 解决以下问题:

已确保根据问题ID for each FormControl提供唯一的名称. Used valueChanges可观察以记录和跟踪控件值的更改. 在每次导航时重新创建FormGroup及其控件,以try 并重置窗体状态.

我正在寻找洞察力或解决方案,以确保每个动态创建的控件正确地反映其在模板中的值,并且准确地检测和记录值更改.任何关于如何解决绑定问题或改进Angular 的动态表单处理的建议都将不胜感激.

Angular CLI:14.2.4 node :21.6.1

编辑:重现我的问题的极简主义代码

Minimal.component.ts

import { FormBuilder, FormGroup, Validators } from '@angular/forms';

interface Question {
  id: string;
  label: string;
}

@Component({
  selector: 'app-minimal-form',
  templateUrl: './Minimal.component.html',
})
export class MinimalFormComponent implements OnInit {
  myForm: FormGroup;
  currentQuestion: Question;
  questions: Question[] = [
    { id: 'A', label: 'Question A' },
    { id: 'B', label: 'Question B' },
  ];
  currentControlName: string;

  constructor(private fb: FormBuilder) {}

  ngOnInit(): void {
    this.currentQuestion = this.questions[0];
    this.currentControlName = `question_${this.currentQuestion.id}`;
    this.myForm = this.fb.group({
      [this.currentControlName]: ['', Validators.required],
    });
  }

  goToNextQuestion(): void {
    const currentIndex = this.questions.findIndex(q => q.id === this.currentQuestion.id);
    const nextIndex = currentIndex + 1;
    if (nextIndex < this.questions.length) {
      this.currentQuestion = this.questions[nextIndex];
      this.currentControlName = `question_${this.currentQuestion.id}`;
      this.myForm = this.fb.group({
        [this.currentControlName]: ['', Validators.required],
      });
    }
  }
}

Minimal.component.html

<form [formGroup]="myForm">
    <label>{{ currentQuestion.label }}</label>
    <input type="text" [formControlName]="currentControlName">
    <button type="button" (click)="goToNextQuestion()">Next Question</button>
</form>

推荐答案

如果您必须使用原来的方法,我建议您只需要一个名为question的控件,并在需要的地方更新/重置值,我们可以使用不同的属性output来跟踪保存的值!

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

interface Question {
  id: string;
  label: string;
}

@Component({
  selector: 'app-minimal-form',
  template: `
    <form [formGroup]="myForm">
      <label>{{ questions[currentQuestionIndex]?.label }}</label>
      <input type="text" formControlName="question">
      <button type="button" (click)="goToNextQuestion()">Next Question</button>
    </form>
    {{output | json}}
  `,
})
export class AppComponent {
  myForm: FormGroup;
  currentQuestion: Question;
  questions: Question[] = [
    { id: 'A', label: 'Question A' },
    { id: 'B', label: 'Question B' },
  ];
  currentControlName: string;
  currentQuestionIndex = 0;
  output: any = {};

  constructor(private fb: FormBuilder) {}

  ngOnInit(): void {
    this.myForm = this.fb.group({
      question: this.fb.control('', Validators.required),
    });
  }

  goToNextQuestion(): void {
    if (this.currentQuestionIndex < this.questions.length) {
      const currentQuestionCtrl = this.myForm.get('question');
      this.output[`question_${this.questions[this.currentQuestionIndex].id}`] =
        currentQuestionCtrl.value;
      currentQuestionCtrl.reset();
      if (this.currentQuestionIndex + 1 !== this.questions.length) {
        this.currentQuestionIndex++;
      }
    }
  }
}

Stackblitz Demo


在HTML上需要与表单控件相关联的输入,我们不能用不同的表单控件名称交换输入并期望它能工作!这是另一种方法,在开始时设置输入,然后使用HTML属性hidden隐藏/显示当前值,这是使用currentQuestionIndex中的索引跟踪的,这似乎很有效!

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

interface Question {
  id: string;
  label: string;
}

@Component({
  selector: 'app-minimal-form',
  template: `
    <form [formGroup]="myForm">
      <ng-container *ngFor="let question of questions; trackBy: track; let i = index">
        <span [hidden]="i !== currentQuestionIndex">
          <label>{{ questions[i].label }}</label>
          <input type="text" [formControlName]="'question_' + question.id">
        </span>
      </ng-container>
      <button type="button" (click)="goToNextQuestion()">Next Question</button>
    </form>

    {{myForm.value | json}}
  `,
})
export class AppComponent {
  myForm: FormGroup;
  currentQuestion: Question;
  questions: Question[] = [
    { id: 'A', label: 'Question A' },
    { id: 'B', label: 'Question B' },
  ];
  currentQuestionIndex: number = 0;

  constructor(private fb: FormBuilder) {}

  track(index: number, item: Question) {
    return item.id;
  }

  ngOnInit(): void {
    this.currentQuestion = this.questions[0];
    this.myForm = this.fb.group({});
    this.currentQuestionIndex = 0;
    this.questions.forEach((question: Question) => {
      this.myForm.addControl(
        `question_${question.id}`,
        this.fb.control('', Validators.required)
      );
    });
  }

  goToNextQuestion(): void {
    this.currentQuestionIndex++;
  }
}

Stackblitz Demo

Html相关问答推荐

如何防止SVG图标出现断行?

D3.js—分组条形图—更新输入数据时出错

如何获得一个背景图像,不是模糊的内部容器,但模糊的外部'

HTML5章节或文章

子元素以元素X开始和结束的元素的XPath?

Tumblr博客:如何修复提交页面和询问页面框列计数问题?

创建一个带有div和径向渐变的全宽半圆

Css网格:两列,除非小于150px

如何使用多边形制作如图所示的背景

如何为高度较小的块制作边框动画?

四开将一个 qmd 文档中的单词链接到另一个 qmd 文档中的单词

如何使用jQuery将我制作的通用头部和底部加载到多个HTML文件中?

使用flex wrap时,如何设置另一个元素的宽度与换行后的元素对齐?

如何在Bootstrap卡片底部添加波浪形状

使用 sprite 的问题,字符 sprite 中的额外边距

是否可以使 huxtable 输出悬停?

水平对齐两列中的两个按钮 同一级别

如何在 blazor 中单击单个按钮调用 2 个等待任务

Bootstrap 5 英雄

Primeng浮动标签溢出管理