Trên thực tế, có hai điều cần thực hiện:
- Một thành phần cung cấp logic của thành phần biểu mẫu của bạn. Nó không phải là đầu vào vì nó sẽ được cung cấp bởi
ngModelchính nó
- Tùy chỉnh
ControlValueAccessorsẽ triển khai cầu nối giữa thành phần này và ngModel/ngControl
Hãy lấy một mẫu. Tôi muốn triển khai một thành phần quản lý danh sách các thẻ cho một công ty. Thành phần sẽ cho phép thêm và bớt các thẻ. Tôi muốn thêm xác thực để đảm bảo rằng danh sách thẻ không trống. Tôi sẽ xác định nó trong thành phần của mình như được mô tả bên dưới:
(...)
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]
});
}
}
Thành TagsComponentphần xác định logic để thêm và bớt các phần tử trong tagsdanh sách.
@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 = '';
}
}
Như bạn thấy, không có đầu vào trong thành phần này nhưng một setValuemột (tên không quan trọng ở đây). Chúng tôi sử dụng nó sau này để cung cấp giá trị từ ngModelthành phần. Thành phần này xác định một sự kiện để thông báo khi trạng thái của thành phần (danh sách thẻ) được cập nhật.
Bây giờ hãy triển khai liên kết giữa thành phần này và ngModel/ ngControl. Điều này tương ứng với một chỉ thị thực hiện ControlValueAccessorgiao diện. Một nhà cung cấp phải được xác định cho trình truy cập giá trị này đối với NG_VALUE_ACCESSORmã thông báo (đừng quên sử dụng forwardRefvì lệnh được xác định sau).
Chỉ thị sẽ đính kèm một bộ nghe sự kiện trên tagsChangesự kiện của máy chủ (tức là thành phần mà chỉ thị được đính kèm, tức là TagsComponent). Các onChangephương pháp sẽ được gọi khi sự kiện xảy ra. Phương thức này tương ứng với phương thức được Angular2 đăng ký. Bằng cách này, nó sẽ nhận biết được các thay đổi và cập nhật tương ứng với kiểm soát biểu mẫu liên quan.
Các writeValueđược gọi khi giá trị ràng buộc trong ngFormđược cập nhật. Sau khi đã tiêm thành phần được đính kèm vào (tức là TagsComponent), chúng ta sẽ có thể gọi nó để chuyển giá trị này (xem setValuephương pháp trước ).
Đừng quên cung cấp CUSTOM_VALUE_ACCESSORcác ràng buộc của chỉ thị.
Đây là mã hoàn chỉnh của tùy chỉnh 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ằng cách này khi tôi xóa tất cả các thành viên tagscủa công ty, validthuộc tính của companyForm.controls.tagsquyền kiểm soát sẽ falsetự động trở thành .
Xem bài viết này (phần "Thành phần tương thích với NgModel") để biết thêm chi tiết: