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
ngModel
chính nó
- Tùy chỉnh
ControlValueAccessor
sẽ 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 TagsComponent
phần xác định logic để thêm và bớt các phần tử trong tags
danh 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 setValue
mộ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ừ ngModel
thà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 ControlValueAccessor
giao 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_ACCESSOR
mã thông báo (đừng quên sử dụng forwardRef
vì lệnh được xác định sau).
Chỉ thị sẽ đính kèm một bộ nghe sự kiện trên tagsChange
sự 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 onChange
phươ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 setValue
phương pháp trước ).
Đừng quên cung cấp CUSTOM_VALUE_ACCESSOR
cá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 tags
của công ty, valid
thuộc tính của companyForm.controls.tags
quyền kiểm soát sẽ false
tự độ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: