我怎样才能创建像native <input>
tag一样工作的自定义组件?我想让我的自定义表单控件能够支持ngControl,ngForm,[(ngModel)].
据我所知,我需要实现一些接口,使我自己的表单控件像本机控件一样工作.
而且,似乎ngForm指令只绑定了<input>
个标签,对吗?我该怎么处理?
让我解释一下为什么我需要这个.我想包装几个输入元素,使它们能够作为一个输入一起工作.还有别的办法吗?
附言:我使用打字脚本.
我怎样才能创建像native <input>
tag一样工作的自定义组件?我想让我的自定义表单控件能够支持ngControl,ngForm,[(ngModel)].
据我所知,我需要实现一些接口,使我自己的表单控件像本机控件一样工作.
而且,似乎ngForm指令只绑定了<input>
个标签,对吗?我该怎么处理?
让我解释一下为什么我需要这个.我想包装几个输入元素,使它们能够作为一个输入一起工作.还有别的办法吗?
附言:我使用打字脚本.
事实上,有两件事需要实施:
ngModel
本身提供ngModel
/ngControl
之间的桥接的自定义ControlValueAccessor
让我们拿一个样品.我想实现一个组件来管理公司的标签列表.该组件将允许添加和删除标记.我想添加一个验证,以确保标记列表不是空的.我将在我的组件中定义它,如下所述:
(...)
import {TagsComponent} from './app.tags.ngform';
import {TagsValueAccessor} from './app.tags.ngform.accessor';
function notEmpty(control) {
if(control.value == null || control.value.length===0) {
return {
notEmpty: true
}
}
return null;
}
@Component({
selector: 'company-details',
directives: [ FormFieldComponent, TagsComponent, TagsValueAccessor ],
template: `
<form [ngFormModel]="companyForm">
Name: <input [(ngModel)]="company.name"
[ngFormControl]="companyForm.controls.name"/>
Tags: <tags [(ngModel)]="company.tags"
[ngFormControl]="companyForm.controls.tags"></tags>
</form>
`
})
export class DetailsComponent implements OnInit {
constructor(_builder:FormBuilder) {
this.company = new Company('companyid',
'some name', [ 'tag1', 'tag2' ]);
this.companyForm = _builder.group({
name: ['', Validators.required],
tags: ['', notEmpty]
});
}
}
TagsComponent
组件定义了在tags
列表中添加和删除元素的逻辑.
@Component({
selector: 'tags',
template: `
<div *ngIf="tags">
<span *ngFor="#tag of tags" style="font-size:14px"
class="label label-default" (click)="removeTag(tag)">
{{label}} <span class="glyphicon glyphicon-remove"
aria- hidden="true"></span>
</span>
<span> | </span>
<span style="display:inline-block;">
<input [(ngModel)]="tagToAdd"
style="width: 50px; font-size: 14px;" class="custom"/>
<em class="glyphicon glyphicon-ok" aria-hidden="true"
(click)="addTag(tagToAdd)"></em>
</span>
</div>
`
})
export class TagsComponent {
@Output()
tagsChange: EventEmitter;
constructor() {
this.tagsChange = new EventEmitter();
}
setValue(value) {
this.tags = value;
}
removeLabel(tag:string) {
var index = this.tags.indexOf(tag, 0);
if (index !== -1) {
this.tags.splice(index, 1);
this.tagsChange.emit(this.tags);
}
}
addLabel(label:string) {
this.tags.push(this.tagToAdd);
this.tagsChange.emit(this.tags);
this.tagToAdd = '';
}
}
正如你所见,这个名字里没有一个重要的输入.我们稍后使用它来提供从ngModel
到组件的值.该组件定义了一个事件,在组件状态(标记列表)更新时通知.
现在让我们实现这个组件和ngModel
/ngControl
之间的链接.这对应于实现ControlValueAccessor
接口的指令.必须针对NG_VALUE_ACCESSOR
令牌为该值访问器定义一个提供程序(不要忘记使用forwardRef
,因为该指令是在之后定义的).
该指令将在主机的tagsChange
事件(即该指令所附加的组件,即TagsComponent
)上附加一个事件侦听器.事件发生时将调用onChange
方法.此方法与Angular2注册的方法相对应.这样,它将了解相关表单控件的更改并相应地更新.
当ngForm
中绑定的值更新时,调用writeValue
.在注入附加的组件(即TagsComponent)后,我们将能够调用它来传递该值(请参阅前面的setValue
方法).
不要忘记在指令的绑定中提供CUSTOM_VALUE_ACCESSOR
.
以下是定制ControlValueAccessor
的完整代码:
import {TagsComponent} from './app.tags.ngform';
const CUSTOM_VALUE_ACCESSOR = CONST_EXPR(new Provider(
NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => TagsValueAccessor), multi: true}));
@Directive({
selector: 'tags',
host: {'(tagsChange)': 'onChange($event)'},
providers: [CUSTOM_VALUE_ACCESSOR]
})
export class TagsValueAccessor implements ControlValueAccessor {
onChange = (_) => {};
onTouched = () => {};
constructor(private host: TagsComponent) { }
writeValue(value: any): void {
this.host.setValue(value);
}
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
}
这样,当我删除公司的所有tags
时,companyForm.controls.tags
控件的valid
属性自动变为false
.
有关更多详细信息,请参阅本文("NgModel兼容组件"一节):