我正在尝试为Angular 2创建自定义复合控件。我的要求是我需要创建一个通用文件选择器控件,允许用户使用html5输入选择 文件[通过输入文件的URL来输入或。
我决定创建通用表单控件,为两个子控件实现ControlValueAccessor接口,因为它们将在其他地方单独使用。
我正在尝试将它们包装在文件 - 选择器 - 本地 - 或 - 远程(缺少更好的单词)控件中。该外部控件应负责发出所选文件的内容,而不关心文件的选择方式。
将值一直传播到消费表单很容易。每次子控件将值传播到中间控件时,中间控件都会使用registerChange回调将其进一步向链中继到消费者。
但是,我无法传播子控件中可能出现的验证错误。我需要将错误一直传播到消费表单,以便它们可以被本地化。
E.g。如果用户在remote-file-picker子控件中输入了无效的url,则该子控件的验证函数将触发正确的错误。该错误被赋予中间控制。如何将此invalid-url错误一直传播到使用表单?
更广泛地说,是否有关于如何在Angular 2中创建复合控件的具体指导?我找不到包装其他自定义控件的自定义控件的任何示例,所以我不确定我是否正确执行。
换句话说,给出:
形式:
outerForm = new FormGroup({
file: new FormControl(null, Validators.required)
});
<form [formGroup]="outerForm">
<File-Picker-Local-Or-Remote formControlName="file"></File-Picker-Local-Or-Remote>
<span class="error">******???******</span>
</form>
文件选取器-LOCAL-OR-远程
innerForm = new FormGroup({
local: new FormControl(),
remote: new FormControl(null, Validators.pattern('http://...'))
});
<input type="file" formControlName="local" />
<input type="text" formControlName="remote" />
当远程子控件验证失败时,它会将错误代码提供给innerForm。如何将这些错误消息传播到外部表单,以便我可以用适当的验证消息替换****** ??? ******?
编辑:我应该注意到,有很多方法可以解决问题或绕过这个问题,包括使用事件发射器构建我自己的解决方案,首先没有复合控制等等。
我真正感兴趣的是创建可重用和可扩展表单控件的 Angular 2方式,消费者可以像任何其他表单控件一样进行交互,并且可以由其他开发人员进一步构建创造更多高级别的控制。
答案 0 :(得分:4)
一位同事和我不久前想到了这一点,但这里有解决其他任何绊脚石的人。
关键是在复合组件中实现ControlValueAccessor和Validator接口。
E.g。
自定义日期控件,实现ControlValueAccessor
@Component({
...
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: CustomDateControl),
multi: true
}]
})
export class CustomDateControl implements ControlValueAccessor {
// implement ControlValueAccessor
}
自定义时间控件,实现ControlValueAccessor
@Component({
...
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: CustomTimeControl),
multi: true
}]
})
export class CustomTimeControl implements ControlValueAccessor {
// implement ControlValueAccessor
}
自定义复合控件 dateTime ,实现ControlValueAccessor 和验证器
@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,并使其validate函数返回子控件错误的某种组合。
我希望这对其他人有用。