【发布时间】:2017-03-02 19:53:24
【问题描述】:
我正在构建一个自定义组件,该组件包含多个表单字段并实现ControlValueAccessor 以与其父组件中的反应式表单进行交互。我还使用NG_VALIDATORS 为自定义表单组件设置验证,基本上遵循本指南:
https://blog.thoughtram.io/angular/2016/07/27/custom-form-controls-in-angular-2.html
然后通过使用*ngIf 包裹在<div> 标记中来打开/关闭子表单。
我面临的问题是,每次显示然后隐藏组件时,验证器功能都会“重新连接”一次。之前对验证器函数的每个引用都会被记住,因此如果要隐藏控件然后再次显示 5 次,则验证器会在每个更改检测周期被触发 5 次,而不是仅仅一次。
感谢任何帮助或指导。
为了演示问题,我在验证函数中添加了console.log。
plunkr:https://plnkr.co/edit/nIj6WR?p=preview
应用组件
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Component, OnInit } from '@angular/core';
import { ReactiveFormsModule, FormGroup, FormBuilder } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
import { ChildFormComponent } from './child-form.component';
@Component({
selector: 'app-root',
template: `
<h2>
Form Value
</h2>
<div>
<pre>
{{form.value | json}}
</pre>
</div>
<form [formGroup]="form">
<label for="value1">Value 1</label>
<input id="value1" type="text" formControlName="value1" />
<h3>Show child form?</h3>
<div>
<label for="showChildFormYes">Yes</label>
<input id="showChildFormYes" type="radio" formControlName="showChildForm" value="yes" />
<label for="showChildFormNo">No</label>
<input id="showChildFormNo" type="radio" formControlName="showChildForm" value="no" />
</div>
<div *ngIf="showChildForm">
<app-child-form formControlName="childValue"></app-child-form>
</div>
</form>
`
})
export class AppComponent implements OnInit {
form: FormGroup;
showChildForm: boolean;
constructor(private formBuilder: FormBuilder) { }
ngOnInit() {
console.log('initializing app form');
this.form = this.formBuilder.group({
value1: '',
childValue: '',
showChildForm: 'no'
});
this.form.get('showChildForm').valueChanges.subscribe(value => {
this.showChildForm = value === 'yes';
});
}
}
@NgModule({
declarations: [
AppComponent,
ChildFormComponent
],
imports: [
BrowserModule,
ReactiveFormsModule,
HttpModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
子表单组件
import {
Component,
OnInit,
OnDestroy,
forwardRef
} from '@angular/core';
import {
FormGroup,
FormControl,
FormBuilder,
ControlValueAccessor,
NG_VALUE_ACCESSOR,
NG_VALIDATORS,
ValidatorFn
} from '@angular/forms';
const CHILD_FORM_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ChildFormComponent),
multi: true
};
const CHILD_FORM_VALIDATORS = {
provide: NG_VALIDATORS,
useExisting: forwardRef(() => ChildFormComponent),
multi: true
};
function validatorFnFactory() {
return (control: FormControl): ValidatorFn => {
console.log('VALIDATING', control.value);
return null;
}
}
@Component({
selector: 'app-child-form',
template: `
<p>
child-form works!
</p>
<form [formGroup]="form">
<input type="text" formControlName="value1" />
</form>
`,
providers: [
CHILD_FORM_VALUE_ACCESSOR,
CHILD_FORM_VALIDATORS
]
})
export class ChildFormComponent implements OnInit, OnDestroy, ControlValueAccessor {
form: FormGroup;
validator: ValidatorFn;
constructor(private formBuilder: FormBuilder) {
this.validator = validatorFnFactory();
}
ngOnInit() {
console.log('initializing child form');
this.form = this.formBuilder.group({
value1: ''
});
this.form.valueChanges.subscribe(value => {
this.value = value;
})
}
ngOnDestroy() {
console.log('destroying child form');
}
validate(control: FormControl) {
this.validator(control);
}
_value: any = '';
get value(): any { return this._value; };
set value(v: any) {
if (v !== this._value) {
this._value = v;
this.onChange(v);
}
}
writeValue(value: any) {
this._value = value;
this.onChange(value);
}
onChange = (_) => { };
onTouched = () => { };
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
}
【问题讨论】: