我和一位同事不久前就解决了这个问题,但这里是其他遇到这个问题的人的解决方案。
关键是在复合组件中实现 ControlValueAccessor 和 Validator 接口。
E.g.
Custom date控制,实现 ControlValueAccessor
@Component({
...
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: CustomDateControl),
multi: true
}]
})
export class CustomDateControl implements ControlValueAccessor {
// implement ControlValueAccessor
}
Custom time控制,实现 ControlValueAccessor
@Component({
...
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: CustomTimeControl),
multi: true
}]
})
export class CustomTimeControl implements ControlValueAccessor {
// implement ControlValueAccessor
}
自定义复合控件dateTime,实现 ControlValueAccessorand验证器
@Component({
...
providers: [{
provide: NG_VALIDATORS,
useExisting: CustomDateTimeControl,
multi: true
}, {
provide: NG_VALUE_ACCESSOR,
useExisting: CustomDateTimeControl,
multi: true
}]
})
export class CustomDateTimeControl implements OnInit, ControlValueAccessor, Validator {
private propagateChange = function (change) { };
private propagateTouched = function () { };
// Inner controls (you can also use an internal FormGroup for this)
public date = new FormControl();
public time = new FormControl();
constructor() {}
ngOnInit() {
this.date.valueChanges
.subscribe(value => {
this.propagateChange(value + ' ' + this.time.value);
this.propagateTouched();
}
this.time.valueChanges
.subscribe(value => {
this.propagateChange(this.date.value + ' ' + value);
this.propagateTouched();
}
}
writeValue(value) {
// Need to update the inner controls, but don't use setValue / patchValue,
// as that will trigger valueChanges in the above subscriptions,
// incorrectly calling touched
}
registerOnChange(fn) {
this.propagateChange = fn;
}
registerOnTouched(fn) {
this.propagateTouched = fn;
}
validate(control) {
// Custom logic to validate the parent control. In this case,
// we may choose to union all childrens' errors.
let errors = Object.assign(this.localControl.errors || {}, this.remoteControl.errors || {});
return Object.keys(errors).length ? errors : null;
}
}
对于我自己最初的问题的回答,将错误冒泡到复合控件链上的一个好方法是在这些复合控件中实现 Validator,并让它们的验证函数返回子控件错误的某种组合。
我希望这对其他人有用。