今天我們要做多選元件checkbox
就先參考昨天radio的寫法
一樣在element下面增加base-checkbox
ng g component base-checkbox
然後在main的設定檔裡面增加一組範例如下
{
name: 'checkbox1',
cname: '多選1',
inputType: 'checkbox',
placeholder: '',
required: true,
defaultValue: '1',
options: [
{ label: '選項1', value: '1' },
{ label: '選項2', value: '2' },
{ label: '選項3', value: '3' },
]
}
接下來一樣在field.component.html增加一組給checkbox的模板
<div *ngSwitchCase="'checkbox'">
<app-base-checkbox
[fieldSetting]="fieldSetting"
[fieldObj]="fieldObj"
></app-base-checkbox>
</div>
然後先參考昨天的寫法寫個一樣的checkbox
// base-checkbox.component.html
<div>
<label class="checkbox" *ngFor="let option of fieldSetting?.options">
<input
type="checkbox"
[(ngModel)]="value"
[name]="fieldSetting.name"
value="{{ option.value }}"
(change)="valueChange()"
/>
<span> <b></b>{{ option.label }}</span>
</label>
</div>
// base-checkbox.component.ts
@Input() fieldSetting!: FieldSetting;
@Input() fieldObj!: any;
value!: string;
constructor() { }
ngOnInit(): void
{
this.value = this.fieldObj[this.fieldSetting.name];
}
valueChange()
{
this.fieldObj[this.fieldSetting.name] = this.value;
}
寫到這邊會發現 首頁上出來的元件好像有點奇怪
點選以後發現是連動的
這是因為我們綁到同一個value
畢竟這邊checkbox會是多選
所以數值紀錄的部分要調整一下寫法
這樣我們把value調整成selectedValue
裡面紀錄我們checkbox有勾選的value
著手開始改動程式
這邊在html會移除的ngModel
ts的話原本的value改成以下
checkedValue!: string[];
options!: any[];
checkedValue是個陣列 但設定檔裡面的defaultValue是字串
不想為了特例去改動設定檔
這邊先暫定規則 用逗號去紀錄已選項目
所以會用split(',') 去把字串解析成陣列
this.checkedValue = this.fieldObj[this.fieldSetting.name].split(',');
selectedValue要怎麼跟options做關聯呢
思考了一下
這邊其實可以在元件裡面拷貝一份options並且增加一個checked的欄位
checked內的數值可以用selectedValue去對比該項目的value
this.options = this.fieldSetting.options?.map(opt => {
return { ...opt, // 沿用原本options裡面的label跟value
checked: !!this.checkedValue.find(c => c === opt.value) } // 直接find處理 並用雙否定轉成boolean值
}
) || []
後面補上 空陣列是因為有可能options會是空 加上個預設值是空陣列的概念
valueChange的部分 這邊如果觸發變動
則回傳對應項目的option.value
這邊的想法是用option.value去找到options裡面的對應項目
然後把checked顛倒過來
let thisOption = this.options.find(opt => opt.value == value)
thisOption.checked = !thisOption.checked;
options變動完會順便把變動的值更新回checkedValue
this.checkedValue = this.options
.filter(opt => opt.checked) // 篩選有勾選的 即checked為true
.map(opt => opt.value) // checkedValue只要記錄value
然後把checkedValue的狀態更新回fieldObj
this.fieldObj[this.fieldSetting.name] = this.checkedValue.join(',');
這樣變更完的base-checkbox.component會是下列這樣
// html
<div>
<label class="checkbox" *ngFor="let option of options">
<input
type="checkbox"
[checked]="option.checked"
[name]="fieldSetting.name"
value="{{ option.value }}"
(change)="valueChange(option.value)"
/>
<span> <b></b>{{ option.label }}</span>
</label>
</div>
//ts
@Input() fieldSetting!: FieldSetting;
@Input() fieldObj!: any;
checkedValue!: string[];
options!: any[];
constructor() { }
ngOnInit(): void
{
this.checkedValue = this.fieldObj[this.fieldSetting.name].split(',');
this.options = this.fieldSetting.options?.map(opt => { return { ...opt, checked: !!this.checkedValue.find(c => c === opt.value) } }) || []
}
valueChange(value: string)
{
let thisOption = this.options.find(opt => opt.value == value);
thisOption.checked = !thisOption.checked;
this.checkedValue = this.options.filter(opt => opt.checked).map(opt => opt.value);
this.fieldObj[this.fieldSetting.name] = this.checkedValue.join(',');
}
以上就是今天的多選元件部分
用了滿多陣列處理的方法
以前看別人寫都覺得這在炫技吧
開始用了以後覺得真的很方便 回不去了
在說明的時候就多加了一些折行方便閱讀
如果有看不懂的的可以在留言討論
今日程式:day09