Mak*_*min 81 typescript angular
如何创建自定义组件,就像本机<input>标签一样?我想让我的自定义表单控件能够支持ngControl,ngForm,[(ngModel)].
据我所知,我需要实现一些接口,使我自己的表单控件工作就像本机一样.
此外,似乎ngForm指令仅绑定<input>标签,这是对的吗?我该如何处理?
让我解释为什么我需要这个.我想包装几个输入元素,使它们能够作为一个单独的输入一起工作.还有其他方法可以解决这个问题吗?再一次:我想让这个控件像原生一样.验证,ngForm,ngModel双向绑定等.
ps:我使用的是Typescript.
Dav*_*vid 90
我不明白为什么我在互联网上找到的每个例子都必须如此复杂.在解释一个新概念时,我认为最简单的工作示例总是最好的.我把它蒸馏了一下:
使用实现ngModel的组件的外部表单的HTML:
EmailExternal=<input [(ngModel)]="email">
<inputfield [(ngModel)]="email"></inputfield>
Run Code Online (Sandbox Code Playgroud)
自包含的组件(没有单独的'访问者'类 - 也许我错过了这一点):
import {Component, Provider, forwardRef, Input} from "@angular/core";
import {ControlValueAccessor, NG_VALUE_ACCESSOR, CORE_DIRECTIVES} from "@angular/common";
const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR = new Provider(
NG_VALUE_ACCESSOR, {
useExisting: forwardRef(() => InputField),
multi: true
});
@Component({
selector : 'inputfield',
template: `<input [(ngModel)]="value">`,
directives: [CORE_DIRECTIVES],
providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class InputField implements ControlValueAccessor {
private _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; }
}
Run Code Online (Sandbox Code Playgroud)
事实上,我刚刚将所有这些内容抽象为一个抽象类,我现在使用ngModel扩展每个组件.对我来说,这是一大堆开销和样板代码,我可以不用.
编辑:这是:
import { forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
export abstract class AbstractValueAccessor implements ControlValueAccessor {
_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;
// warning: comment below if only want to emit on user intervention
this.onChange(value);
}
onChange = (_) => {};
onTouched = () => {};
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
}
export function MakeProvider(type : any){
return {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => type),
multi: true
};
}
Run Code Online (Sandbox Code Playgroud)
这是一个使用它的组件:(TS):
import {Component, Input} from "@angular/core";
import {CORE_DIRECTIVES} from "@angular/common";
import {AbstractValueAccessor, MakeProvider} from "../abstractValueAcessor";
@Component({
selector : 'inputfield',
template: require('./genericinput.component.ng2.html'),
directives: [CORE_DIRECTIVES],
providers: [MakeProvider(InputField)]
})
export class InputField extends AbstractValueAccessor {
@Input('displaytext') displaytext: string;
@Input('placeholder') placeholder: string;
}
Run Code Online (Sandbox Code Playgroud)
HTML:
<div class="form-group">
<label class="control-label" >{{displaytext}}</label>
<input [(ngModel)]="value" type="text" placeholder="{{placeholder}}" class="form-control input-md">
</div>
Run Code Online (Sandbox Code Playgroud)
Thi*_*ier 79
实际上,有两件事要实现:
ngModel它自己提供ControlValueAccessor,将实现此组件和ngModel/ 之间的桥梁ngControl我们来试试吧.我想实现一个管理公司标签列表的组件.该组件将允许添加和删除标签.我想添加一个验证,以确保标签列表不为空.我将在我的组件中定义它,如下所述:
(...)
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]
});
}
}
Run Code Online (Sandbox Code Playgroud)
该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 != undefined) {
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 = '';
}
}
Run Code Online (Sandbox Code Playgroud)
如您所见,此组件中没有输入,只有setValue一个(此处名称并不重要).我们稍后使用它来提供ngModel组件的值.此组件定义在更新组件状态(标记列表)时通知的事件.
现在让我们实现这个组件和ngModel/ 之间的链接ngControl.这对应于实现ControlValueAccessor接口的指令.必须根据NG_VALUE_ACCESSOR令牌为此值访问器定义提供程序(不要忘记使用,forwardRef因为后面定义了该指令).
该指令将tagsChange在主机事件上附加一个事件监听器(即指令附加的组件,即TagsComponent).onChange事件发生时将调用该方法.此方法对应于Angular2注册的方法.这样,它将意识到相应的表单控件的更改和更新.
在writeValue当绑定的值称为ngForm被更新.在已经注入安装在(即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; }
}
Run Code Online (Sandbox Code Playgroud)
这样当我删除所有tags公司时,控件的valid属性companyForm.controls.tags就会false自动变为.
有关更多详细信息,请参阅此文章("与NgModel兼容的组件"一节):
小智 17
RC5版本链接中有一个示例:http://almerosteyn.com/2016/04/linkup-custom-control-to-ngcontrol-ngmodel
import { Component, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
const noop = () => {
};
export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomInputComponent),
multi: true
};
@Component({
selector: 'custom-input',
template: `<div class="form-group">
<label>
<ng-content></ng-content>
<input [(ngModel)]="value"
class="form-control"
(blur)="onBlur()" >
</label>
</div>`,
providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class CustomInputComponent implements ControlValueAccessor {
//The internal data model
private innerValue: any = '';
//Placeholders for the callbacks which are later providesd
//by the Control Value Accessor
private onTouchedCallback: () => void = noop;
private onChangeCallback: (_: any) => void = noop;
//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.onChangeCallback(v);
}
}
//Set touched on blur
onBlur() {
this.onTouchedCallback();
}
//From ControlValueAccessor interface
writeValue(value: any) {
if (value !== this.innerValue) {
this.innerValue = value;
}
}
//From ControlValueAccessor interface
registerOnChange(fn: any) {
this.onChangeCallback = fn;
}
//From ControlValueAccessor interface
registerOnTouched(fn: any) {
this.onTouchedCallback = fn;
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们可以使用此自定义控件,如下所示:
<form>
<custom-input name="someValue"
[(ngModel)]="dataModel">
Enter data:
</custom-input>
</form>
Run Code Online (Sandbox Code Playgroud)
蒂埃里的例子很有帮助.以下是TagsValueAccessor运行所需的导入...
import {Directive, Provider} from 'angular2/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR } from 'angular2/common';
import {CONST_EXPR} from 'angular2/src/facade/lang';
import {forwardRef} from 'angular2/src/core/di';
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
75348 次 |
| 最近记录: |