Sebenarnya, ada dua hal yang harus diterapkan:
- Komponen yang menyediakan logika komponen formulir Anda. Ini tidak memerlukan masukan karena akan disediakan dengan
ngModelsendirinya
- Sebuah kebiasaan
ControlValueAccessoryang akan mengimplementasikan jembatan antara komponen ini dan ngModel/ngControl
Mari kita ambil contoh. Saya ingin menerapkan komponen yang mengelola daftar tag untuk perusahaan. Komponen akan memungkinkan untuk menambah dan menghapus tag. Saya ingin menambahkan validasi untuk memastikan bahwa daftar tag tidak kosong. Saya akan mendefinisikannya di komponen saya seperti yang dijelaskan di bawah ini:
(...)
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]
});
}
}
The TagsComponentkomponen mendefinisikan logika untuk menambah dan menghapus elemen dalam tagsdaftar.
@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 = '';
}
}
Seperti yang Anda lihat, tidak ada input di komponen ini kecuali setValuesatu (namanya tidak penting di sini). Kami menggunakannya nanti untuk memberikan nilai dari ngModelke komponen. Komponen ini mendefinisikan peristiwa untuk diberitahukan ketika status komponen (daftar tag) diperbarui.
Mari kita implementasikan sekarang tautan antara komponen ini dan ngModel/ ngControl. Ini sesuai dengan arahan yang mengimplementasikan ControlValueAccessorantarmuka. Penyedia harus ditentukan untuk pengakses nilai ini terhadap NG_VALUE_ACCESSORtoken (jangan lupa untuk menggunakan forwardRefkarena direktif ditentukan setelahnya).
Direktif akan melampirkan event listener pada tagsChangeacara host (yaitu komponen direktif dilampirkan, yaitu TagsComponent). The onChangemetode akan dipanggil saat peristiwa itu terjadi. Metode ini sesuai dengan yang didaftarkan oleh Angular2. Dengan cara ini ia akan mengetahui perubahan dan pembaruan yang sesuai dengan kontrol formulir yang terkait.
The writeValuedisebut ketika nilai terikat dalam ngFormdiperbarui. Setelah memasukkan komponen yang dilampirkan (mis. TagsComponent), kita akan dapat memanggilnya untuk meneruskan nilai ini (lihat setValuemetode sebelumnya ).
Jangan lupa untuk memberikan CUSTOM_VALUE_ACCESSORdi binding dari direktif tersebut.
Berikut ini kode lengkap custom 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; }
}
Dengan cara ini ketika saya menghapus semua tagsperusahaan, validatribut companyForm.controls.tagskontrol menjadi falsesecara otomatis.
Lihat artikel ini (bagian "komponen yang kompatibel dengan NgModel") untuk detail selengkapnya: