Angular 响应式表单数组,更改检测覆盖表单
Angular Reactive Form Array, Change detection overriding form
我使用以下 作为我的 FormArray 表单设计的基础。我试图做的事情的要点是使表单与页面上其他地方的更改保持同步,但在此表单中切换 boolean/checkbox。 (用户有他们 select 的卡片列表,此表格显示此 selection 的列表)
不幸的是,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);
}
总结一下:我需要一种方法来使用从输入绑定构建的 formarrays,以跟上变化而不会快速覆盖表单。
根据angular的文档
OnChanges: A lifecycle hook that is called when any data-bound property of a directive changes
话虽如此,覆盖表单的既不是变更检测也不是 onChanges 挂钩。作为最佳实践,我们应该只构建一次表单并使用 FormArray 的方法来干扰数组
您可以直接在数组上使用 FormArray 方法和 push
项,而不是重建表单。我假设您面临的问题是您正在重建表格,无论数据是什么。
我的意思是:认为你有两个组成部分。 Child 和 Parent。 child 负责操作数据(添加、删除),而 parent 负责在表单上显示这些数据。尽管这样做似乎微不足道,但您必须过滤掉任何已被 Child 组件处理过的项目。
在您的实施中,尽量不要 rebuild
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/Whosebug-53836673
这不是一个完整的示例,但它有助于理解问题的重点。
我希望我理解正确你面临的问题是什么
我使用以下
不幸的是,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);
}
总结一下:我需要一种方法来使用从输入绑定构建的 formarrays,以跟上变化而不会快速覆盖表单。
根据angular的文档
OnChanges: A lifecycle hook that is called when any data-bound property of a directive changes
话虽如此,覆盖表单的既不是变更检测也不是 onChanges 挂钩。作为最佳实践,我们应该只构建一次表单并使用 FormArray 的方法来干扰数组
您可以直接在数组上使用 FormArray 方法和 push
项,而不是重建表单。我假设您面临的问题是您正在重建表格,无论数据是什么。
我的意思是:认为你有两个组成部分。 Child 和 Parent。 child 负责操作数据(添加、删除),而 parent 负责在表单上显示这些数据。尽管这样做似乎微不足道,但您必须过滤掉任何已被 Child 组件处理过的项目。
在您的实施中,尽量不要 rebuild
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/Whosebug-53836673
这不是一个完整的示例,但它有助于理解问题的重点。
我希望我理解正确你面临的问题是什么