ControlValueAccessor
一般我們要實現一個自定義表單控件,首先要做的就是實現 ControlValueAccessor
接口。
ControlValueAccessor 是一個連接表單模型和視圖(DOM元素)的接口,自定義的表單控件必須實現這個接口,它的作用是:
- 把 form 模型中值映射到視圖中
- 當視圖發生變化時,通知 form directives 或 form controls
這個接口提供了以下方法:
interface ControlValueAccessor {
writeValue(obj: any): void //數據由模型更新到視圖(model->view)時,方法被調用
registerOnChange(fn: any): void //數據由視圖更新到模型(view->model)時,方法被調用
registerOnTouched(fn: any): void
setDisabledState(isDisabled: boolean)?: void
}
NG_VALUE_ACCESSOR
利用這一 Token
可將控件注冊成為可讓表單訪問到其值的控件,使用方法如下:
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CheckboxGroupComponent),
multi: true
}]
Note:Token NG_VALIDATORS 可將控件注冊成為可讓表單訪問到其驗證狀態的控件。
自定義表單控件 CheckboxGroupComponent
1. 控件模板(基於Ionic)
<ion-grid>
<ion-row>
<label>{{ groupName }}</label>
<ion-col *ngFor="let e of options">
<section>
<mat-checkbox [disabled]="disabled"
[checked]="this.model.length && this.model.indexOf(e.id) > -1"
[value]="e[value]"
(change)="setValue(e)">
{{ e[display] }}
</mat-checkbox>
</section>
</ion-col>
</ion-row>
</ion-grid>
2.組件類
export class CheckboxGroupComponent implements ControlValueAccessor {
@Input() options: any = [] //checkbox數據數組 包含 id/value/display 三個屬性
@Input() disabled: boolean = false //控件可用性
@Input() display: string //display是展示到界面的名稱
@Input() value: string //value是提交到后台的數據
@Input() groupName: string //checkboxgroup的標題
model: any = [] //存放每個checked value 實時變化
onChange = (_: any) => {}
onTouched = () => {}
constructor() {
}
setValue(obj){ //復選框的狀態改變時觸發
let { id } = obj //解構賦值 這里也可以寫value
let index = this.model.indexOf(id)
if(index > -1){
//model中若存在則移除
this.model.splice(index, 1)
//onChange方法將值的改變傳遞給外部 如果不調用這一方法表單控件中得不到任何數據
//這個this.model也就是控件使用時與 ngModel 綁定的值
this.onChange(this.model)
}else{
//不存在則添加
this.model.push(id)
this.onChange(this.model)
}
console.log(this.model)
}
writeValue(value: any){
if(value && value.length){
this.model = value
}else {
this.model = []
}
}
registerOnChange(fn: any){
this.onChange = fn
}
registerOnTouched(fn: any){
this.onTouched = fn
}
}
3. 注冊為表單控件
@Component({
selector: 'checkbox-group',
templateUrl: 'checkbox-group.html',
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CheckboxGroupComponent),
multi: true
}]
})
4. 控件使用
<checkbox-group [groupName]="'愛好:'" [options]="hobbies" [display]="'title'"
[value]="'value'" [(ngModel)]="hobby" formControlName="hobby">
</checkbox-group>
<p>{{inform.get('hobby').value | json}}</p>
hobbies = [
{ id: 1, title: '電影', value: 'movie' },
{ id: 2, title: '音樂', value: 'song' },
{ id: 3, title: '登山', value: 'mountain' },
{ id: 4, title: '閱讀', value: 'read' },
{ id: 5, title: '游泳', value: 'swim' },
]
使用響應式表單,控件名為hobby
,顯示效果如下: