Angular 12 - Nest-Form 列表中的组件触发 NG0100 错误
Angular 12 - Nest-Form component in list trigger NG0100 Error
版本:Angular12.2
堆栈闪电战:https://stackblitz.com/edit/angular-ivy-r8cpbh?file=src/app/app.component.html
你好,
我有一个包含 3 个组件类型的项目:
- Parent 有 Children 列表
- Child 有子Child人名单
- 子Child独立组件
在 Parent 级别我们可以添加 Child
在Child级别我们可以添加SubChild
一切正常,但当我添加新行时,有时(当有效字段更改时)我在 formGroup.invalid 上收到此消息:
NG0100: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value for 'disabled': 'false'. Current value: 'true'
我知道这是一个常见的错误,但是这次我没有找到pb来自哪里...
请帮忙。
Parent
|____Child
|______SubChild
|______SubChild
|______SubChild
|____Child
|______SubChild
|____Child
|______SubChild
|______SubChild
Parent ts
public data!: ParentData;
public formGroup!: FormGroup;
get children() {
return <FormArray>this.formGroup.controls.children;
}
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.data = {
parentField1: 'string',
parentField2: 'string',
parentHiddenField1: 'string',
children: []
};
this.formGroup = this.toFormGroup(this.data);
this.formGroup.valueChanges.subscribe(value => console.log(value));
}
add() {
this.children.push(new FormGroup({}));
this.data.children.push({
id: 1,
childField1: 'string',
subchildren: []
});
}
private toFormGroup(data: ParentData): FormGroup {
const formGroup = this.fb.group({
parentField1: [data.parentField1, Validators.required],
parentField2: [data.parentField2, Validators.required],
parentHiddenField1: [data.parentHiddenField1],
children: new FormArray([])
});
return formGroup;
}
submit(finalData: ParentData) {
console.log(finalData);
}
}
Parent Html
<form [formGroup]="formGroup" (ngSubmit)="submit(formGroup.value)">
<label for="parentField1">Parent Field 1</label>
<input formControlName="parentField1" />
<br />
<label for="parentField2">Parent Field 2</label>
<input formControlName="parentField2" />
<div formArrayName="children">
<button type="button" (click)="add()">+</button>
<div *ngFor="let child of data.children; let i=index">
<div>Parent</div>
<app-test-list-child [formGroup]="children.controls[i] | formGroup" [data]="child">
</app-test-list-child>
</div>
</div>
<button type="submit" [disabled]="formGroup.invalid">Send</button>
</form>
Child ts
export class TestListChildComponent implements OnInit, AfterViewInit {
@Input('formGroup')
public formGroup!: FormGroup;
@Input('data')
public data!: ChildData;
get subchildren() {
return <FormArray>this.formGroup.get('subchildren');
}
constructor(private cd: ChangeDetectorRef) {}
ngOnInit() {
this.formGroup.addControl(
'childField1',
new FormControl(this.data.childField1 || '', Validators.required)
);
this.formGroup.addControl('subchildren', new FormArray([]));
}
ngAfterViewInit() {}
add() {
this.subchildren.push(new FormGroup({}));
this.data.subchildren.push({
childField1: 'test',
childHiddenField1: '',
childField2: '',
id: 1
});
}
}
Child html
<div [formGroup]="formGroup">
<input formControlName="childField1" />
<button type="button" (click)="add()">++</button>
<div formArrayName="subchildren">
<div *ngFor="let child of subchildren.controls; let i = index">
<div>Child</div>
<app-test-child [formGroup]="child | formGroup" [data]="data.subchildren[i]">
</app-test-child>
</div>
</div>
</div>
子Child ts
export class TestChildComponent implements OnInit, AfterViewInit {
@Input('formGroup')
public formGroup!: FormGroup;
@Input('data')
public data!: SubChildData;
constructor(private fb: FormBuilder) { }
ngOnInit() {
this.toFormGroup(this.data);
}
ngAfterViewInit() {
}
private toFormGroup(data: SubChildData) {
this.formGroup.addControl("childField1", new FormControl(data?.childField1 || '', Validators.required));
this.formGroup.addControl("childField2", new FormControl(data?.childField2 || '', Validators.required));
}
}
亚Child html
<!-- child-form.component.html -->
SubChild
<div [formGroup]="formGroup">
<label for="childField1">Child Field 1</label>
<input formControlName="childField1" />
<br/>
<label for="childField1">Child Field 2</label>
<input formControlName="childField2" />
</div>
型号
export interface ParentData {
parentField1: string;
parentField2: string;
parentHiddenField1: string;
children: ChildData[];
}
export interface ChildData {
id: number;
childField1: string;
subchildren: SubChildData[];
}
export interface SubChildData {
id: number;
childField1: string;
childField2: string;
childHiddenField1: string;
}
管道
@Pipe({
name: 'formGroup'
})
export class FormGroupPipe implements PipeTransform {
transform(value: AbstractControl, ...args: unknown[]): FormGroup {
return value as FormGroup;
}
}
错误发生是因为在 test-child-component.ts 中你有一个必需的验证器:
private toFormGroup(data: SubChildData) {
this.formGroup.addControl("childField1", new FormControl(data?.childField1 || '', Validators.required));
this.formGroup.addControl("childField2", new FormControl(data?.childField2 || '', Validators.required));
}
...但在 test-list-component.ts 中,您没有为 childField2 控件分配值,并且表单无效。
最简单的解决方法是为第二个控件分配一个默认值:
add() {
this.subchildren.push(new FormGroup({}));
this.data.subchildren.push({
childField1: 'test',
childHiddenField1: '',
childField2: 'default value',
id: 1
});
}
我最终发现 pb 是某种异步模式...
我在创建表单时添加了 setTimeout,现在一切正常。
Child
setTimeout(() => {
this.formGroup.addControl(
'childField1',
new FormControl(this.data.childField1 || '', Validators.required)
);
this.formGroup.addControl('subchildren', new FormArray([]));
});
亚Child
setTimeout(() => {
this.formGroup.addControl("childField1", new FormControl(data?.childField1 || '', Validators.required));
this.formGroup.addControl("childField2", new FormControl(data?.childField2 || '', Validators.required));
});
在 add
子“++”方法的末尾添加 this.cdr.detectChanges()
将修复它。
版本:Angular12.2 堆栈闪电战:https://stackblitz.com/edit/angular-ivy-r8cpbh?file=src/app/app.component.html
你好, 我有一个包含 3 个组件类型的项目:
- Parent 有 Children 列表
- Child 有子Child人名单
- 子Child独立组件
在 Parent 级别我们可以添加 Child
在Child级别我们可以添加SubChild
一切正常,但当我添加新行时,有时(当有效字段更改时)我在 formGroup.invalid 上收到此消息:
NG0100: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value for 'disabled': 'false'. Current value: 'true'
我知道这是一个常见的错误,但是这次我没有找到pb来自哪里...
请帮忙。
Parent
|____Child
|______SubChild
|______SubChild
|______SubChild
|____Child
|______SubChild
|____Child
|______SubChild
|______SubChild
Parent ts
public data!: ParentData;
public formGroup!: FormGroup;
get children() {
return <FormArray>this.formGroup.controls.children;
}
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.data = {
parentField1: 'string',
parentField2: 'string',
parentHiddenField1: 'string',
children: []
};
this.formGroup = this.toFormGroup(this.data);
this.formGroup.valueChanges.subscribe(value => console.log(value));
}
add() {
this.children.push(new FormGroup({}));
this.data.children.push({
id: 1,
childField1: 'string',
subchildren: []
});
}
private toFormGroup(data: ParentData): FormGroup {
const formGroup = this.fb.group({
parentField1: [data.parentField1, Validators.required],
parentField2: [data.parentField2, Validators.required],
parentHiddenField1: [data.parentHiddenField1],
children: new FormArray([])
});
return formGroup;
}
submit(finalData: ParentData) {
console.log(finalData);
}
}
Parent Html
<form [formGroup]="formGroup" (ngSubmit)="submit(formGroup.value)">
<label for="parentField1">Parent Field 1</label>
<input formControlName="parentField1" />
<br />
<label for="parentField2">Parent Field 2</label>
<input formControlName="parentField2" />
<div formArrayName="children">
<button type="button" (click)="add()">+</button>
<div *ngFor="let child of data.children; let i=index">
<div>Parent</div>
<app-test-list-child [formGroup]="children.controls[i] | formGroup" [data]="child">
</app-test-list-child>
</div>
</div>
<button type="submit" [disabled]="formGroup.invalid">Send</button>
</form>
Child ts
export class TestListChildComponent implements OnInit, AfterViewInit {
@Input('formGroup')
public formGroup!: FormGroup;
@Input('data')
public data!: ChildData;
get subchildren() {
return <FormArray>this.formGroup.get('subchildren');
}
constructor(private cd: ChangeDetectorRef) {}
ngOnInit() {
this.formGroup.addControl(
'childField1',
new FormControl(this.data.childField1 || '', Validators.required)
);
this.formGroup.addControl('subchildren', new FormArray([]));
}
ngAfterViewInit() {}
add() {
this.subchildren.push(new FormGroup({}));
this.data.subchildren.push({
childField1: 'test',
childHiddenField1: '',
childField2: '',
id: 1
});
}
}
Child html
<div [formGroup]="formGroup">
<input formControlName="childField1" />
<button type="button" (click)="add()">++</button>
<div formArrayName="subchildren">
<div *ngFor="let child of subchildren.controls; let i = index">
<div>Child</div>
<app-test-child [formGroup]="child | formGroup" [data]="data.subchildren[i]">
</app-test-child>
</div>
</div>
</div>
子Child ts
export class TestChildComponent implements OnInit, AfterViewInit {
@Input('formGroup')
public formGroup!: FormGroup;
@Input('data')
public data!: SubChildData;
constructor(private fb: FormBuilder) { }
ngOnInit() {
this.toFormGroup(this.data);
}
ngAfterViewInit() {
}
private toFormGroup(data: SubChildData) {
this.formGroup.addControl("childField1", new FormControl(data?.childField1 || '', Validators.required));
this.formGroup.addControl("childField2", new FormControl(data?.childField2 || '', Validators.required));
}
}
亚Child html
<!-- child-form.component.html -->
SubChild
<div [formGroup]="formGroup">
<label for="childField1">Child Field 1</label>
<input formControlName="childField1" />
<br/>
<label for="childField1">Child Field 2</label>
<input formControlName="childField2" />
</div>
型号
export interface ParentData {
parentField1: string;
parentField2: string;
parentHiddenField1: string;
children: ChildData[];
}
export interface ChildData {
id: number;
childField1: string;
subchildren: SubChildData[];
}
export interface SubChildData {
id: number;
childField1: string;
childField2: string;
childHiddenField1: string;
}
管道
@Pipe({
name: 'formGroup'
})
export class FormGroupPipe implements PipeTransform {
transform(value: AbstractControl, ...args: unknown[]): FormGroup {
return value as FormGroup;
}
}
错误发生是因为在 test-child-component.ts 中你有一个必需的验证器:
private toFormGroup(data: SubChildData) {
this.formGroup.addControl("childField1", new FormControl(data?.childField1 || '', Validators.required));
this.formGroup.addControl("childField2", new FormControl(data?.childField2 || '', Validators.required));
}
...但在 test-list-component.ts 中,您没有为 childField2 控件分配值,并且表单无效。
最简单的解决方法是为第二个控件分配一个默认值:
add() {
this.subchildren.push(new FormGroup({}));
this.data.subchildren.push({
childField1: 'test',
childHiddenField1: '',
childField2: 'default value',
id: 1
});
}
我最终发现 pb 是某种异步模式...
我在创建表单时添加了 setTimeout,现在一切正常。
Child
setTimeout(() => {
this.formGroup.addControl(
'childField1',
new FormControl(this.data.childField1 || '', Validators.required)
);
this.formGroup.addControl('subchildren', new FormArray([]));
});
亚Child
setTimeout(() => {
this.formGroup.addControl("childField1", new FormControl(data?.childField1 || '', Validators.required));
this.formGroup.addControl("childField2", new FormControl(data?.childField2 || '', Validators.required));
});
在 add
子“++”方法的末尾添加 this.cdr.detectChanges()
将修复它。