iT邦幫忙

2024 iThome 鐵人賽

DAY 27
0

昨天我們增加了彈窗規則
今天要針對我們的需求做進一步調整
現在比較麻煩的是我們想要傳一個模板進到彈窗裡面
這個模板會是我們寫好的元件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()">&times;</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


上一篇
第26天 自製彈窗
下一篇
第28天 設定設定檔
系列文
簡單的事 最困難-Angular動態Form元件30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言