Sebenarnya, ada dua hal yang harus diterapkan:
- Komponen yang menyediakan logika komponen formulir Anda. Ini tidak memerlukan masukan karena akan disediakan dengan
ngModel
sendirinya
- Sebuah kebiasaan
ControlValueAccessor
yang 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 TagsComponent
komponen mendefinisikan logika untuk menambah dan menghapus elemen dalam tags
daftar.
@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 setValue
satu (namanya tidak penting di sini). Kami menggunakannya nanti untuk memberikan nilai dari ngModel
ke 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 ControlValueAccessor
antarmuka. Penyedia harus ditentukan untuk pengakses nilai ini terhadap NG_VALUE_ACCESSOR
token (jangan lupa untuk menggunakan forwardRef
karena direktif ditentukan setelahnya).
Direktif akan melampirkan event listener pada tagsChange
acara host (yaitu komponen direktif dilampirkan, yaitu TagsComponent
). The onChange
metode 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 writeValue
disebut ketika nilai terikat dalam ngForm
diperbarui. Setelah memasukkan komponen yang dilampirkan (mis. TagsComponent), kita akan dapat memanggilnya untuk meneruskan nilai ini (lihat setValue
metode sebelumnya ).
Jangan lupa untuk memberikan CUSTOM_VALUE_ACCESSOR
di 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 tags
perusahaan, valid
atribut companyForm.controls.tags
kontrol menjadi false
secara otomatis.
Lihat artikel ini (bagian "komponen yang kompatibel dengan NgModel") untuk detail selengkapnya: