我使用过以下question作为我 FormArray 表单设计的基础。我想做的事情的要点是使表单与页面上其他地方的更改保持最新,但在此表单中切换 bool 值/复选框。 (用户有一个他们选择的卡片列表,此表单显示了此选择的列表)
不幸的是,ngOnChanges
似乎在不断更新表单,而我的更改正在被覆盖。在我的构造函数中,我检测值更改,并意图发出这些更改。然而,
this.contextSummaryForm.dirty
始终为假。 rebuildForm() 上的断点表明该方法每秒被调用多次 - 因此将 contextItem.isEditEnable 更改为 false 被完全覆盖。我可以阅读我的逻辑并了解为什么会发生这种情况 - 但我真的不明白我应该做什么来允许从其父组件更新 contextList 并允许用户在此处更新表单。
构造函数和更改检测
@Input()
contextList: ContextItem[];
@Output()
contextListChange = new EventEmitter<any>();
valueChangeSubscription = new Subscription;
contextSummaryForm: FormGroup;
isLoaded: boolean = false;
constructor(protected fb: FormBuilder) {
this.createForm();
this.valueChangeSubscription.add(
this.contextSummaryForm.valueChanges
.debounceTime(environment.debounceTime)
.subscribe((values) => {
if (this.isLoaded && this.contextSummaryForm.valid && this.contextSummaryForm.dirty) {
this.contextSummaryForm.value.plans.forEach(x => {
var item = this.contextList.find(y => y.plan.id === x.id);
item.isEditEnabled = x.isEditEnabled;
});
this.contextListChange.emit(this.contextList);
this.contextSummaryForm.markAsPristine();
}
}));
}
表单创建:
createForm(): void {
this.contextSummaryForm = this.fb.group({
plans: this.fb.array([this.initArrayRows()])
});
}
initArrayRows(): FormGroup {
return this.fb.group({
id: [''],
name: [''],
isEditEnabled: [''],
});
}
OnChanges
ngOnChanges(changes: SimpleChanges) {
for (let propName in changes) {
if (propName === 'contextList') {
if (this.contextList) {
this.rebuildForm();
this.isLoaded = true;
}
}
}
}
rebuildForm() {
this.contextSummaryForm.reset({
});
//this.fillInPlans();
this.setPlans(this.contextList);
}
setPlans(items: ContextItem[]) {
let control = this.fb.array([]);
items.forEach(x => {
control.push(this.fb.group({
id: x.plan.id,
name: x.plan.Name,
isEditEnabled: x.isEditEnabled,
}));
});
this.contextSummaryForm.setControl('plans', control);
}
总结一下:我需要一种使用从输入绑定(bind)构建的表单数组的方法,该方法可以跟上变化而不会快速覆盖表单。
最佳答案
根据 Angular 的文档
OnChanges: A lifecycle hook that is called when any data-bound property of a directive changes
话虽如此,覆盖表单的不是更改检测,也不是 onChanges Hook 。作为最佳实践,我们应该只构建一次表单并使用 FormArray 的方法来干扰数组
您可以利用 FormArray 方法并直接在数组上push
项,而不是重建表单。我认为您面临的问题是您正在重建表单,无论数据是什么。
我的意思是:认为你有两个组件。 child 和家长。子级负责操作数据(添加、删除),父级负责在表单上显示这些数据。虽然这样做看起来很简单,但您必须过滤掉子组件已经处理过的所有项目。
在您的实现中,尽量不要在 onChanges 上重建
表单,而是将项目推送到数组上。
您应该将哪些项目推送到数组? (过滤掉所有已经处理过的项目)
const contextItemsToInsert =
this.contextList.filter((it: any) => !this.plans.value.map(pl => pl.id).includes(it.id));
这是一种可以解决您的问题的方法
ngOnChanges(changes: SimpleChanges) {
for (let propName in changes) {
if (propName === 'contextList') {
const contextItemsToInsert =
this.contextList.filter((it: any) => !this.plans.value.map(pl => pl.id).includes(it.id));
contextItemsToInsert.forEach((x: any) => {
this.plans.push(this.fb.group({
id: x.id,
name: x.name,
isEditEnabled: x.isEditEnabled,
}))
})
// similar approach for deleted items
}
}
}
ngOnInit() {
this.createForm();
}
createForm(): void {
this.contextSummaryForm = this.fb.group({
plans: this.fb.array([this.initArrayRows()])
});
}
initArrayRows(): FormGroup {
return this.fb.group({
id: [''],
name: [''],
isEditEnabled: ['']
});
}
在这里您可以找到一个工作示例 https://stackblitz.com/edit/stackoverflow-53836673
这不是一个完整的示例,但它有助于理解问题的要点。
我希望我正确理解您面临的问题是什么
关于 Angular react 形式数组,更改检测覆盖形式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53836673/