编辑:这是我的 Stackblitz 应用程序的 编辑器 url,它以简化的设置显示了第一个控件和第二个控件之间的值“转移”问题。
我正在 Angular 中开发动态表单,其中表单结构根据用户输入而变化。表单中的每个问题都由 FormGroup 中的唯一控件表示,并且当用户浏览问题时动态创建或更新控件。我的方法包括更新 currentControlName 并在导航上重新创建表单控件(使用 nextPage 和 prevPage 方法),以确保每个问题在表单中都有自己独特的控件。
方法:
我使用一项服务来根据所选服务获取问题。 该表单在 ngOnInit 中初始化,第一个问题作为当前问题。 在问题之间导航时,我为每个问题创建一个新的 FormControl,并使用问题 ID 通过唯一名称进行标识。 我将答案存储在一个对象中,以便即使在问题之间来回导航时也能保留答案。
问题:
输入第一个问题的答案并导航到第二个问题后,第二个问题的输入字段错误地显示为第一个问题输入的值。此外,更新第二个问题的值不会触发预期的 valueChanges observable,并且表单控件和模板之间似乎存在不匹配或绑定问题。
具体:
第一个输入控件按预期工作,记录值变化。 导航到第二个问题并更新其控件后,没有记录任何更改,这表明可能无法为新创建的控件正确设置可观察对象。 尽管在每个导航上重新创建了表单组和控件,但模板中控件值的绑定似乎始终存在问题。
尝试解决:
根据问题 ID 确保每个 FormControl 的名称唯一。 使用 valueChanges observable 来记录和跟踪控制值的变化。 在每次导航时重新创建 FormGroup 及其控件,以尝试重置表单状态。
我正在寻找见解或解决方案,以确保每个动态创建的控件正确反映其在模板中的值,并准确检测和记录值更改。任何关于如何解决绑定问题或改进 Angular 中的动态表单处理的建议将不胜感激。
Angular CLI:14.2.4 节点:21.6.1
编辑:用极简代码重现我的问题
最小.组件.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>
需要一个与 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