Aslında uygulanacak iki şey vardır:
- Form bileşeninizin mantığını sağlayan bir bileşen.
ngModelKendisi tarafından sağlanacağı için bir girdiye ihtiyaç duymaz.
ControlValueAccessorBu bileşen ve ngModel/ veya arasındaki köprüyü uygulayacak bir gelenekngControl
Bir örnek alalım. Bir şirket için bir etiket listesini yöneten bir bileşen uygulamak istiyorum. Bileşen, etiketlerin eklenmesine ve kaldırılmasına izin verecektir. Etiket listesinin boş olmadığından emin olmak için bir doğrulama eklemek istiyorum. Bunu bileşenimde aşağıda açıklandığı gibi tanımlayacağım:
(...)
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]
});
}
}
TagsComponentBileşen ekleyip öğeleri kaldırmak için mantık tanımlar tagslistede.
@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 = '';
}
}
Gördüğünüz gibi, bu bileşende bir girdi dışında bir girdi var setValue(isim burada önemli değil). Daha sonra ngModelbileşenden bileşene değer sağlamak için kullanırız . Bu bileşen, bileşenin (etiketler listesi) durumu güncellendiğinde bildirilecek bir olay tanımlar.
Şimdi bu bileşen ve ngModel/ arasındaki bağlantıyı uygulayalım ngControl. Bu, ControlValueAccessorarayüzü uygulayan bir yönergeye karşılık gelir . NG_VALUE_ACCESSORBelirteç karşısında bu değer erişimcisi için bir sağlayıcı tanımlanmalıdır ( forwardRefyönerge sonradan tanımlandığı için kullanmayı unutmayın ).
Yönerge tagsChange, ana bilgisayar olayına bir olay dinleyicisi ekleyecektir (yani yönergenin eklendiği bileşen, yani TagsComponent). onChangeOlay meydana geldiğinde yöntemi çağrılır. Bu yöntem, Angular2 tarafından kaydedilene karşılık gelir. Bu şekilde, ilgili form kontrolüne göre değişikliklerin ve güncellemelerin farkında olacaktır.
İçindeki writeValuedeğerin değeri ngFormgüncellendiğinde çağrılır . Eklenen bileşeni (yani, TagsComponent) enjekte ettikten sonra, bu değeri iletmek için onu çağırabileceğiz (önceki setValueyönteme bakın).
CUSTOM_VALUE_ACCESSORDirektifin bağlayıcılarını sağlamayı unutmayın .
İşte özelliğin tam kodu 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; }
}
Böylelikle tagsşirketin tamamını kaldırdığımda valid, companyForm.controls.tagskontrolün niteliği falseotomatik olarak oluyor.
Daha fazla ayrıntı için bu makaleye bakın ("NgModel uyumlu bileşen" bölümü):