今天我們要來寫另一個組合元件
我們有個需求是需要一組radiobutton的是或否
如果勾是 則後面的文字欄才可以輸入 不然則disabled
在調整元件之前 我們之前已經寫了不少function
有些應該可以重複使用
所以這邊我們決定來創個shareService來存放
首先我們把form-input的function搬進來 做一些調整
/**
* 透過name 取得欄位設定值
* @param fieldSetting
* @param name
* @returns FieldSetting
*/
getSetting(fieldSettings: FieldSetting[], name: string, type: string = 'name'): FieldSetting
{
return fieldSettings.find((obj: any) => obj[type] == name) || { cname: '', name: '', inputType: '' }
}
/**
* 透過表單從傳入設定職組取得符合表單的設定值
* @param settings 設定植
* @param fg 表單
* @returns FieldSetting[]
*/
getSettingsByForm(settings: FieldSetting[], fg: FormGroup): FieldSetting[]
{
return Object.keys(fg.value).map(v =>
{
return settings.find(setting => setting.name === v) as FieldSetting;
})
}
getControl(group: FormGroup, columnName: string)
{
return group.get(columnName) as FormControl
}
typeof(value: any)
{
return typeof value;
}
combo-phone有個getSettingByGroupType
其實跟getSettingByName有點像
我們這邊改成getSetting type是第三個參數
非必填 如果沒填就是用name
調整後的form-input.component.html會是這樣
<div>
{{ pageSetting.title }}
<div [formGroup]="pageSetting.form!" *ngIf="pageSetting.form" class="row">
<div *ngFor="let fieldObj of getGroupList(innerForm)" class="col-6">
<ng-container *ngIf="shareService.typeof(fieldObj) === 'string'">
<app-field-template
[fieldSetting]="
shareService.getSetting(pageSetting.fieldSettings, fieldObj)
"
[control]="shareService.getControl(pageSetting.form, fieldObj)"
>
</app-field-template>
<!-- {{ getControl(fieldObj).value }} -->
</ng-container>
<ng-container *ngIf="shareService.typeof(fieldObj) !== 'string'">
<app-combo-phone
[fieldSettings]="
shareService.getSettingsByForm(pageSetting.fieldSettings, fieldObj)
"
[inputForm]="fieldObj"
>
</app-combo-phone>
</ng-container>
</div>
</div>
</div>
調整完form-input以後 來調整combo-phone
//combo-phone.component.ts
constructor(public shareService: ShareService) { }
ngOnInit(): void
{
}
getControl(group: FormGroup, groupType: string)
{
return this.shareService.getControl(group, this.getSettingByGroupType(groupType).name);
}
// 用name取得setting
getSettingByGroupType(groupType: string)
{
return this.shareService.getSetting(this.fieldSettings, groupType, 'groupType');
}
html不需要調整 用shareService簡化寫法
調整完了以後現在來增加新的元件
我們先增加設定檔
{
name: 'radioTextRadio1',
cname: '控制輸入欄位',
inputType: 'radio',
defaultValue: 'true',
groupName: 'radioText1',
groupType: 'radioText-radio',
options: [
{ label: '是', value: 'true' },
{ label: '否', value: 'false' },
]
},
{
name: 'radioTextText1',
cname: '控制輸入欄位',
inputType: 'text',
groupName: 'radioText1',
groupType: 'radioText-text',
placeholder: '',
defaultValue: '勾選後才可輸入',
validator: [Validators.maxLength(3)]
},
理論上現在設定檔會產生innerForm進而產生畫面
所以這個設定檔會產生一個radioText1的formGroup
不過現在沒有特別判斷 我們只有一個combo-phone
這樣這個formGroup會丟到combo-phone裡面
然後會因為沒有對應的groupType所以造成頁面故障
所以這邊我們要先調整一下form-input
進而判斷我們要用哪一個combo
那我們要怎麼判斷呢
這邊我們會有一個getSettingsByForm的settings
透過查詢裡面的groupType可以得知這符合哪個屬性
為了方便處理 我們再多包一層元件app-combo-field-template
// form-input.component.html
<app-combo-field-template
[fieldSettings]="
shareService.getSettingsByForm(pageSetting.fieldSettings, fieldObj)
"
[inputForm]="fieldObj"
>
</app-combo-field-template>
// combo-field-template.ts
@Input() fieldSettings!: FieldSetting[];
@Input() inputForm!: FormGroup;
constructor() { }
// 查詢是否為某個groupType
findFieldSetting(value: string)
{
return !!this.fieldSettings.find(setting => { return setting.groupType?.startsWith(value) })
}
// combo-field-template.html
<div>
<div *ngIf="findFieldSetting('phone')">
<app-combo-phone [fieldSettings]="fieldSettings" [inputForm]="inputForm">
</app-combo-phone>
</div>
<div *ngIf="findFieldSetting('radioText')">
<app-combo-radio-text
[fieldSettings]="fieldSettings"
[inputForm]="inputForm"
>
</app-combo-radio-text>
</div>
</div>
由於我groupType的命名邏輯是以關鍵字為開頭
所以這邊使用startsWith
這邊要補充說明一點
groupName是用來組成組合元件用的
同一個組合元件的groupName會一樣 命名沒有特殊規則
groupType是組合元件後 對應元件內的對應位置用的
基本上會對應組合元件內的元件 要依循元件規則命名
上面也可以看到我們多加了一個combo-radio-text元件
傳入的數值跟cobmo-phone一樣
// combo-radio-text.component.html
<div>
<div class="row">
<span>
{{ getSettingByGroupType("radioText-radio").cname }}
</span>
<div class="col-4">
<app-field
[fieldSetting]="getSettingByGroupType('radioText-radio')"
[control]="getControl(inputForm, 'radioText-radio')"
></app-field>
</div>
<div class="col-8">
<app-field
[fieldSetting]="getSettingByGroupType('radioText-text')"
[control]="getControl(inputForm, 'radioText-text')"
[disabled]="getControl(inputForm, 'radioText-radio').value == 'false'"
></app-field>
</div>
</div>
</div>
ts的部分跟combo-phone一樣就不贅述
html的部分 因為這次的元件用到兩種不同的基本元件
這時候才想到 應該要用包裝好的app-field阿
另外這次會需要用到disabled的部分
所以在field.component裡面也要增加disabled這個input
@Input() fieldSetting!: FieldSetting;
@Input() control!: FormControl;
@Input() disabled = false;
constructor() { }
ngOnChanges(): void
{
this.fieldSetting.disabled = this.disabled;
}
這邊我們增加disabled這個input
但如果要一個一個往下傳的話每個base元件都要調整
好在我們base現在已經吃fieldSetting了
我們就在fieldSetting裡面增加一個disabled屬性
然後這邊要注意 原本的ngOnInit沒用到
這邊我們改成使用ngOnChanges
差異是ngOnInit只會在初始化的時候渲染一次
ngOnChanges是input變動的時候都會執行
因為我們的disabled會變動 所以需要做一個更新到fieldSetting的動作
// base-text.component.html
<div>
<input
type="text"
placeholder="{{ fieldSetting.placeholder || '文字輸入框' }}"
[formControl]="control"
[attr.disabled]="fieldSetting.disabled ? '' : null"
[ngClass]="{ 'is-invalid': control.invalid && control.touched }"
/>
<span *ngIf="getError()" style="color: red">{{ getError() }}</span>
</div>
這邊我們用attr.disabled
然後給予空白與null
這是之前多方嘗試以後才知道
這邊要控制attr.disabled要失效要給null
給false沒有作用
原本想說加個元件這篇應該很短
結果牽涉到要重構就越改越多了
不過重構完以後要添加新的元件就方便多了
明天再來討論一個滿常見到又比較複雜的 地址元件
今日程式:day20