我有以下带有表单的子组件,并且当父组件提交主表单时,需要在我的控制下显示错误消息。
import {
Component, ChangeDetectorRef, forwardRef,
NgModule, OnInit
} from '@angular/core';
import {
FormGroup, FormControl, FormBuilder, Validators,
NG_VALIDATORS, Validator, AbstractControl, ValidationErrors
} from '@angular/forms';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
export const ADDRESS_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AddressFormComponent),
multi: true
};
export const ADDRESS_VALIDATORS: any = {
provide: NG_VALIDATORS,
useExisting: forwardRef(() => AddressFormComponent),
multi: true
};
@Component({
selector: 'app-address-form',
templateUrl: 'address-form.component.html',
providers: [ADDRESS_VALUE_ACCESSOR, ADDRESS_VALIDATORS],
})
export class AddressFormComponent implements OnInit, ControlValueAccessor, Validator {
addressForm: FormGroup;
private innerValue: any;
onModelTouched: Function = () => { };
onModelChange: Function = () => { };
constructor(
private _fb: FormBuilder,
private ref: ChangeDetectorRef,
) { }
ngOnInit() {
this.addressForm = this._fb.group({
'via': new FormControl('', Validators.required),
'civico': new FormControl(''),
'cap': new FormControl(''),
'comune': new FormControl(''),
'provincia': new FormControl(''),
'regione': new FormControl(''),
'stato': new FormControl(''),
'frazione': new FormControl('')
});
}
// get accessor
get value(): any {
return this.innerValue;
}
// set accessor including call the onchange callback
set value(v: any) {
if (v !== this.innerValue) {
this.innerValue = v;
this.onModelChange();
}
}
// Set touched on blur
onBlur() {
this.onModelTouched();
}
writeValue(value: any): void {
this.value = value;
if (this.value != null) {
this.addressForm.get('via').setValue(this.value.via);
this.addressForm.get('civico').setValue(this.value.civico);
this.addressForm.get('cap').setValue(this.value.cap);
this.addressForm.get('comune').setValue(this.value.comune);
this.addressForm.get('provincia').setValue(this.value.provincia);
this.addressForm.get('frazione').setValue(this.value.frazione);
this.addressForm.get('stato').setValue(this.value.stato);
this.addressForm.get('regione').setValue(this.value.regione);
}
this.ref.markForCheck();
}
registerOnChange(fn: Function): void {
this.addressForm.valueChanges.subscribe(() => {
fn(this.addressForm.value);
});
this.onModelChange = fn;
}
registerOnTouched(fn: Function): void {
this.onModelTouched = fn;
}
validate(c: AbstractControl): ValidationErrors | null {
return this.addressForm.valid ? null : { subformerror: 'Problems in subform!' };
}
registerOnValidatorChange(fn: () => void): void {
this.addressForm.statusChanges.subscribe(fn);
}
}
@NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule
],
exports: [AddressFormComponent],
declarations: [
AddressFormComponent
],
entryComponents: [AddressFormComponent],
providers: [
]
})
export class AddressFormModule { }
查看:
<form [formGroup]="addressForm">
<div class="row">
<div class="form-group col-md-10">
<label for="txtVia">Via</label>
<input type="text" pInputText class="form-control" id="txtVia"
formControlName="via">
<div *ngIf="!addressForm.controls['via'].valid && (addressForm.controls['via'].dirty || addressForm.controls['via'].touched)"
class="alert alert-danger">
Campo obbligatorio
</div>
</div>
<div class="form-group col-md-2">
<label for="txtCivico">Civico</label>
<input type="text" pInputText class="form-control" id="txtCivico"
formControlName="civico">
</div>
</div>
<div class="row">
<div class="form-group col-md-3">
<label for="txtCap">Cap</label>
<input type="text" pInputText class="form-control" id="txtCap"
formControlName="cap">
</div>
<div class="form-group col-md-6">
<label for="txtComune">Comune</label>
<input type="text" pInputText class="form-control" id="txtComune"
formControlName="comune">
</div>
<div class="form-group col-md-3">
<label for="txtProvincia">Provincia</label>
<input type="text" pInputText class="form-control" id="txtProvincia"
formControlName="provincia">
</div>
</div>
<div class="form-group">
<label for="txtFrazione">Frazione</label>
<input type="text" pInputText class="form-control" id="txtFrazione"
formControlName="frazione">
</div>
<div class="row">
<div class="form-group col-md-6">
<label for="txtRegione">Regione</label>
<input type="text" pInputText class="form-control" id="txtRegione"
formControlName="regione">
</div>
<div class="form-group col-md-6">
<label for="txtStato">Stato</label>
<input type="text" pInputText class="form-control" id="txtStato"
formControlName="stato">
</div>
</div>
</form>
我通过以下方式将其绑定到父表单:
视图:
<form (submit)="onConfirm($event)" *ngIf="aziendaform" [formGroup]="aziendaform">
<app-address-form formControlName="indirizzoSpedizione"></app-address-form>
</form>
代码:
import { Component, Input, Output, EventEmitter, OnInit, SimpleChanges, OnChanges, ChangeDetectorRef } from '@angular/core';
import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';
@Component({
selector: 'app-azienda-form',
templateUrl: 'aziende-form.component.html'
})
export class AziendeFormComponent implements OnInit, OnChanges {
aziendaform: FormGroup;
constructor(
private fb: FormBuilder,
private cdr: ChangeDetectorRef
) {
}
ngOnChanges(changes: SimpleChanges) {
}
ngOnInit(): void {
this.aziendaform = this.fb.group({
'indirizzoSpedizione': new FormControl(null)
});
this.cdr.detectChanges();
}
validateAllFormFields(formGroup: FormGroup) {
Object.keys(formGroup.controls).forEach(field => {
const control = formGroup.get(field);
if (control instanceof FormControl) {
control.markAsTouched({ onlySelf: true });
} else if (control instanceof FormGroup) {
this.validateAllFormFields(control);
}
});
}
onConfirm(event) {
event.preventDefault();
if (this.aziendaform.valid) {
/*
do somithing
*/
} else {
this.validateAllFormFields(this.aziendaform);
}
}
}
当我按下确认按钮时,该表单无效,但是在子组件中没有错误消息。
似乎markAsTouched
不会传播为子表单。
有人可以帮我吗?
更新
我在这里准备了StackBlitz: https://stackblitz.com/edit/angular-4qcsox
如您所见,确认并重置按钮不会影响子组件。
答案 0 :(得分:2)
如果您单步执行validateAllFormFields()
,将会看到indirizzoSpedizione保持为空,子控件将永远不会被连线。
我通过在父组件中建立一个空表单组来使其工作:
this.aziendaform = this.fb.group({
'indirizzoSpedizione':new FormGroup({})
});
然后让子组件接受一个FormGroup作为输入
export class AddressFormComponent implements OnInit, ControlValueAccessor, Validator {
@Input() addressForm: FormGroup;
并通过addControl添加控件
ngOnInit() {
this.addressForm.addControl('via', new FormControl('', Validators.required));
父组件然后传递空的formGroup
<app-address-form [addressForm]="aziendaform.controls['indirizzoSpedizione']"></app-address-form>
这是您提出的stackBlitz解决方案 https://stackblitz.com/edit/angular-stm9kw