昨天我們增加了彈窗規則
今天要針對我們的需求做進一步調整
現在比較麻煩的是我們想要傳一個模板進到彈窗裡面
這個模板會是我們寫好的元件form-input
這樣我們可以考慮用service去傳遞模板
這樣我們就開始改寫
/// dialog.service.ts
export class DialogService
{
private dialogVisibility = new Subject<boolean>(); // 管理彈窗可見性的Subject
private templateSubject = new Subject<TemplateRef<any>>();
dialogVisibility$ = this.dialogVisibility.asObservable(); // 讓其他組件訂閱可見性狀態
template$ = this.templateSubject.asObservable();
openDialog(template: TemplateRef<any>)
{
this.templateSubject.next(template); // 傳遞模板
this.dialogVisibility.next(true); // 顯示彈窗
}
closeDialog()
{
this.dialogVisibility.next(false); // 關閉彈窗
}
}
增加templateRef相關的程式
// dialog.component.html
<div class="modal" *ngIf="isVisible">
<div class="modal-content">
<span class="close-button" (click)="close()">×</span>
<ng-container *ngTemplateOutlet="currentTemplate"></ng-container>
<!-- 可插入的自定義內容 -->
</div>
</div>
// dialog.component.ts
export class DialogComponent implements OnInit
{
isVisible: boolean = false; // 本地的彈窗狀態
currentTemplate: any; // 接收來自服務的模板
templateName!: string;
constructor(private dialogService: DialogService) { }
ngOnInit(): void
{
// 訂閱服務的可見性狀態變更
this.dialogService.dialogVisibility$.subscribe(isVisible =>
{
this.isVisible = isVisible;
});
// 訂閱模板內容
this.dialogService.template$.subscribe(template =>
{
this.currentTemplate = template;
});
}
close()
{
this.dialogService.closeDialog(); // 調用服務關閉彈窗
}
}
component的部分可以直接傳過來的模板
元件的部分 想要留著前幾天做的
所以把原本groupType的list改成listBasic 新的命名listPopup
// form-input裡面的
if (setting.groupType == 'list' && !!setting.groupName)
// 改成
if (setting.groupType?.startsWith('list') && !!setting.groupName)
// combo-field-template.component.html
// 調整成兩組
<div *ngIf="findFieldSetting('listBasic')">
<app-list [fieldSettings]="fieldSettings" [inputForm]="inputFormGroup">
</app-list>
</div>
<div *ngIf="findFieldSetting('listPopup')">
<app-list-pop [fieldSettings]="fieldSettings" [inputForm]="inputFormGroup">
</app-list-pop>
</div>
新增list-pop.component
// list-pop.component.html
<table [formGroup]="inputForm" style="width: 100%">
<thead>
<tr>
<th *ngFor="let title of titleArr">
{{ title }}
</th>
<th>
<button (click)="openAdd()">新增</button>
</th>
</tr>
</thead>
<tbody formArrayName="{{ listKey }}">
<tr
*ngFor="let row of inputFormArray.controls; let i = index"
[formGroupName]="i"
>
<ng-container
*ngTemplateOutlet="
displayRow;
context: { inTeplateForm: row, index: i }
"
></ng-container>
</tr>
</tbody>
</table>
<ng-template #displayRow let-fg="inTeplateForm" let-i="index">
<td *ngFor="let key of Object.keys(fg.value)">
{{ fg.get(key)?.value }}
</td>
<button (click)="openEdit(i)">編輯</button>
<button (click)="delete(i)">刪除</button>
</ng-template>
<ng-template #add>
<app-form-input [pageSetting]="dialogPagingSetting"></app-form-input>
<button (click)="addSubmit()">送出</button>
</ng-template>
<ng-template #edit>
<app-form-input [pageSetting]="dialogPagingSetting"></app-form-input>
<button (click)="editSubmit()">送出</button>
</ng-template>
沿用之前list.component.html
然後把編輯的面板都移除掉 只留下displayRow
th的地方加新增按鈕
下面增加兩個模板add跟edit
模板內會使用form-input 而傳入的dialogPagingSetting
會是subFormGroup跟相關的設定檔
export class ListPopComponent implements OnInit
{
// 增加viewChild 使面板可以傳進dialogService
@ViewChild('add') add!: TemplateRef<any>;
@ViewChild('edit') edit!: TemplateRef<any>;
@Input() fieldSettings!: FieldSetting[];
@Input() inputForm!: any;
innerFormArray!: FormArray;
subFormGroup!: FormGroup;
titleArr!: string[];
Object = Object;
editRowNum = 1;
dialogPagingSetting!: PageSetting;
get listKey()
{
return Object.keys(this.inputForm.value)[0]
}
get inputFormArray()
{
return this.inputForm.get(this.listKey) as FormArray
}
constructor(
private fb: FormBuilder,
private validatorService: ValidatorService,
public shareService: ShareService,
private dialogService: DialogService,
) { }
ngOnInit(): void
{
if (this.subFormGroup) return;
this.titleArr = [];
this.innerFormArray = new FormArray<FormGroup>([]);
let defaultValue!: string[];
this.subFormGroup = new FormGroup({});
this.fieldSettings.forEach(setting =>
{
if (setting.inputType != 'list')
{
let newControl = this.fb.control(setting.defaultValue) as FormControl;
if (setting.validator && setting.validator?.length > 0)
{
if (!!setting.validator.find(v => v == 'required')) setting.required = true;
newControl.addValidators(this.validatorService.getValidators(setting.validator));
}
this.titleArr.push(setting.cname);
this.subFormGroup.addControl(setting.name, newControl);
} else
{
defaultValue = setting.defaultValue! as string[];
}
})
// 組裝pagingSetting給dialog用
this.dialogPagingSetting = {
title: '',
form: this.subFormGroup,
fieldSettings: this.fieldSettings
.filter(setting => setting.inputType != 'list').map(setting =>
{
let result = { ...setting }
delete result.groupName;
delete result.groupType;
return result;
})
}
if (this.inputFormArray.length > 0) return;
defaultValue.forEach((value: any) =>
{
this.subFormGroup.reset();
this.subFormGroup.patchValue(value);
this.innerFormArray.push(this.fb.group(this.subFormGroup.value));
})
this.inputFormArray.clear();
this.innerFormArray.value.forEach((group: AbstractControl) =>
{
this.inputFormArray.push(this.fb.group(group));
});
this.subFormGroup.reset();
}
// 原本新增編輯的方法 調整成 開視窗 以及 關閉視窗相關的方法
openAdd()
{
this.dialogService.openDialog(this.add);
}
addSubmit()
{
this.inputFormArray.push(this.fb.group(this.subFormGroup.value));
this.dialogService.closeDialog();
this.subFormGroup.reset();
}
openEdit(index: number)
{
this.editRowNum = index;
this.dialogService.openDialog(this.edit);
this.subFormGroup.patchValue(this.inputFormArray.controls[index].value);
console.log('subFormGroup', this.subFormGroup.value);
}
editSubmit()
{
this.inputFormArray.controls[this.editRowNum].patchValue(this.subFormGroup.value);
this.dialogService.closeDialog();
this.subFormGroup.reset();
}
delete(index: number)
{
this.inputFormArray.removeAt(index);
}
}
如上面註解
這邊最主要的是控制彈窗傳遞進去的資料
如果處理得當可以透過formGroup達到我們要的需求
在組裝pagingSetting的時候
form可以直接使用subFormGroup
fieldSettings的話原本subFormGroup的列表不能拿來直接用
要過濾掉第一筆inputType為list的
以及這邊因為是要給form-input用
我們需要攤平list的資料
所以這邊用map把groupName跟groupType過濾掉
這邊我們有些是用引用 有些是用拷貝
引用的話物件會連動
但有些時候不希望物件連動的時候就是用拷貝
像元件用formGroup傳遞的時候滿多情況會希望能引用
不過像fieldSetting這邊有時候就要拷貝 才不會影響到原本的檔案
另外form-input原本沒有處理傳遞form進去這件事
所以我們這邊在做一些調整
ngOnChanges(): void
{
//增加這段
if (!this.pageSetting.form)
{
this.pageSetting.form = this.fb.group({});
}
this.innerForm = this.fb.group({});
this.pageSetting.fieldSettings.forEach(setting =>
{
// 增加這段
let newControl = this.fb.control(this.pageSetting.form?.get(setting.name)?.value || setting.defaultValue) as FormControl;
我們增加了兩段
第一段pageSetting.form undefined的時候初始化
第二段 如果pageSetting.form的對應欄位有值的時候不填預設值
這邊不小心也是寫得有點複雜
如果看了有想法的人也可以提出來討論
今日程式:day27