Angular 8 / Material : 子表单未显示错误
Angular 8 / Material : subform not showing errors
我用这种方式创建了一个带有条件子表单的表单:
父表中:
<form [formGroup]="layerFormGroup" (submit)="submit()" #f="ngForm">
...
<div *ngIf="layerFormGroup.value.mode == 'features'">
<app-edit-layer-features formControlName="modeFormGroup"></app-edit-layer-features>
</div>
与 TS:
ngOnInit() {
this.layerFormGroup = this.formBuilder.group({
...
modeFormGroup: ['', Validators.required]
});
}
public submit() {
if (!this.layerFormGroup.valid) {
return;
}
则子窗体为:
<ng-container [formGroup]="modeFormGroup">
<mat-horizontal-stepper>
<mat-step label="Collection" [stepControl]="modeFormGroup">
<mat-form-field>
<mat-label>Collection</mat-label>
<mat-select formControlName="collectionCtrl">
<mat-option value="col1">col1</mat-option>
<mat-option value="col2">col2</mat-option>
</mat-select>
</mat-form-field>
<div>
<button mat-button matStepperNext type="button">Next</button>
</div>
</mat-step>
<mat-step label="Rendered geometry" [stepControl]="modeFormGroup">
<mat-form-field>
<mat-label>Rendered geometry</mat-label>
<mat-select formControlName="geometryCtrl">
<mat-option value="geoshape">a geopoint</mat-option>
<mat-option value="geopoint">a geoshape</mat-option>
</mat-select>
</mat-form-field>
<div>
<button mat-button matStepperNext type="button">Next</button>
</div>
</mat-step>
</mat-horizontal-stepper>
最后是子视图:
@Component({
selector: 'app-edit-layer-features',
templateUrl: './edit-layer-features.component.html',
styleUrls: ['./edit-layer-features.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => EditLayerFeaturesComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => EditLayerFeaturesComponent),
multi: true
}
]
})
// ControlValueAccessor: see https://christianlydemann.com/form-validation-with-controlvalueaccessor/
export class EditLayerFeaturesComponent implements OnInit, ControlValueAccessor, Validator {
public modeFormGroup: FormGroup = this.formBuilder.group({
collectionCtrl: ['', Validators.required],
geometryCtrl: ['', Validators.required],
});
constructor(
private formBuilder: FormBuilder) { }
ngOnInit() {
}
public onTouched: () => void = () => { };
writeValue(obj: any): void {
if (obj) {
this.modeFormGroup.patchValue(obj, { emitEvent: false });
this.onTouched();
}
}
registerOnChange(fn: any): void {
this.modeFormGroup.valueChanges.subscribe(fn);
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
isDisabled ? this.modeFormGroup.disable() : this.modeFormGroup.enable();
}
validate(control: AbstractControl): import('@angular/forms').ValidationErrors {
return this.modeFormGroup.valid ? null : { invalidForm: { valid: false, message: 'Features fields are invalid' } };
}
registerOnValidatorChange?(fn: () => void): void {
this.modeFormGroup.valueChanges.subscribe(fn);
}
}
问题如下:当我在父表单中调用 submit() 方法时,父表单中添加了 mat-error 标签,但子表单中没有添加,i.a。验证错误显示在父表单中,但不显示在子表单中。
经过一些测试,我发现调用
this.modeFormGroup.markAllAsTouched()
在子表单中确实会显示子错误。无论如何,我应该从父 submit()
方法调用它,但我找不到如何去做(this.layerFormGroup.get('modeFormGroup').valid
没有完成这项工作)。
我觉得我遗漏了一些与 material 有关的东西,有什么线索吗?
谢谢。
编辑 1:我创建了一个 stackblitz,您可以在其中看到问题:https://stackblitz.com/edit/angular-eldyyc。如果您只选择模式 = Features
并单击 save layer
,则只有 name
显示为错误,子表单字段不会显示
问题是组件之间的通信和对原始父表单组的引用。你看,你在父组件中用 modeFormGroup
创建表单组作为一个没有任何控件的嵌套表单组。然后,您不会将对 layerFormGroup
的引用传递给子组件。
另一方面,在您创建本地的子组件中 属性 modeFormGroup
。这个modeFormGroup
和layerFormGroup
中的modeFormGroup
不一样。
this.layerFormGroup = this.formBuilder.group({
name: ['', Validators.required],
mode: ['', Validators.required],
id: [''],
modeFormGroup: this.formBuilder.group({}) //not the same as in the child component
});
请注意,modeFormGroup
是一个空表单组,没有任何控件。现在,在子组件中,您将子表单组创建为:
// totally different form group
public modeFormGroup: FormGroup = this.formBuilder.group({
collectionCtrl: ['', Validators.required],
geometryCtrl: ['', Validators.required]
});
你需要做的是,一旦子组件被初始化,在 ngOnInit()
函数中使用 EventEmitter
发出创建的表单组,并将表单组分配给 modeFormGroup
父窗体组中的控件。
我编辑了你的stackblitz
我用这种方式创建了一个带有条件子表单的表单:
父表中:
<form [formGroup]="layerFormGroup" (submit)="submit()" #f="ngForm">
...
<div *ngIf="layerFormGroup.value.mode == 'features'">
<app-edit-layer-features formControlName="modeFormGroup"></app-edit-layer-features>
</div>
与 TS:
ngOnInit() {
this.layerFormGroup = this.formBuilder.group({
...
modeFormGroup: ['', Validators.required]
});
}
public submit() {
if (!this.layerFormGroup.valid) {
return;
}
则子窗体为:
<ng-container [formGroup]="modeFormGroup">
<mat-horizontal-stepper>
<mat-step label="Collection" [stepControl]="modeFormGroup">
<mat-form-field>
<mat-label>Collection</mat-label>
<mat-select formControlName="collectionCtrl">
<mat-option value="col1">col1</mat-option>
<mat-option value="col2">col2</mat-option>
</mat-select>
</mat-form-field>
<div>
<button mat-button matStepperNext type="button">Next</button>
</div>
</mat-step>
<mat-step label="Rendered geometry" [stepControl]="modeFormGroup">
<mat-form-field>
<mat-label>Rendered geometry</mat-label>
<mat-select formControlName="geometryCtrl">
<mat-option value="geoshape">a geopoint</mat-option>
<mat-option value="geopoint">a geoshape</mat-option>
</mat-select>
</mat-form-field>
<div>
<button mat-button matStepperNext type="button">Next</button>
</div>
</mat-step>
</mat-horizontal-stepper>
最后是子视图:
@Component({
selector: 'app-edit-layer-features',
templateUrl: './edit-layer-features.component.html',
styleUrls: ['./edit-layer-features.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => EditLayerFeaturesComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => EditLayerFeaturesComponent),
multi: true
}
]
})
// ControlValueAccessor: see https://christianlydemann.com/form-validation-with-controlvalueaccessor/
export class EditLayerFeaturesComponent implements OnInit, ControlValueAccessor, Validator {
public modeFormGroup: FormGroup = this.formBuilder.group({
collectionCtrl: ['', Validators.required],
geometryCtrl: ['', Validators.required],
});
constructor(
private formBuilder: FormBuilder) { }
ngOnInit() {
}
public onTouched: () => void = () => { };
writeValue(obj: any): void {
if (obj) {
this.modeFormGroup.patchValue(obj, { emitEvent: false });
this.onTouched();
}
}
registerOnChange(fn: any): void {
this.modeFormGroup.valueChanges.subscribe(fn);
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
isDisabled ? this.modeFormGroup.disable() : this.modeFormGroup.enable();
}
validate(control: AbstractControl): import('@angular/forms').ValidationErrors {
return this.modeFormGroup.valid ? null : { invalidForm: { valid: false, message: 'Features fields are invalid' } };
}
registerOnValidatorChange?(fn: () => void): void {
this.modeFormGroup.valueChanges.subscribe(fn);
}
}
问题如下:当我在父表单中调用 submit() 方法时,父表单中添加了 mat-error 标签,但子表单中没有添加,i.a。验证错误显示在父表单中,但不显示在子表单中。
经过一些测试,我发现调用
this.modeFormGroup.markAllAsTouched()
在子表单中确实会显示子错误。无论如何,我应该从父 submit()
方法调用它,但我找不到如何去做(this.layerFormGroup.get('modeFormGroup').valid
没有完成这项工作)。
我觉得我遗漏了一些与 material 有关的东西,有什么线索吗?
谢谢。
编辑 1:我创建了一个 stackblitz,您可以在其中看到问题:https://stackblitz.com/edit/angular-eldyyc。如果您只选择模式 = Features
并单击 save layer
,则只有 name
显示为错误,子表单字段不会显示
问题是组件之间的通信和对原始父表单组的引用。你看,你在父组件中用 modeFormGroup
创建表单组作为一个没有任何控件的嵌套表单组。然后,您不会将对 layerFormGroup
的引用传递给子组件。
另一方面,在您创建本地的子组件中 属性 modeFormGroup
。这个modeFormGroup
和layerFormGroup
中的modeFormGroup
不一样。
this.layerFormGroup = this.formBuilder.group({
name: ['', Validators.required],
mode: ['', Validators.required],
id: [''],
modeFormGroup: this.formBuilder.group({}) //not the same as in the child component
});
请注意,modeFormGroup
是一个空表单组,没有任何控件。现在,在子组件中,您将子表单组创建为:
// totally different form group
public modeFormGroup: FormGroup = this.formBuilder.group({
collectionCtrl: ['', Validators.required],
geometryCtrl: ['', Validators.required]
});
你需要做的是,一旦子组件被初始化,在 ngOnInit()
函数中使用 EventEmitter
发出创建的表单组,并将表单组分配给 modeFormGroup
父窗体组中的控件。
我编辑了你的stackblitz