簡體   English   中英

具有反應形式和 formArray 的表中的角度列級過濾器

[英]Angular Column level filters in table having reactive form and formArray

我想實現高度動態的表格,其中應用了列級過濾器,並實現了對表格的每個單元格應用驗證的可編輯行。

我已經實現了帶有可編輯行和動態驗證的動態表格顯示。 但是在使用列級過濾器時苦苦掙扎。

我的問題陳述:

  • UI 將接收表格標題以顯示和對應表格行數據。 例如headers = ['name','age']和數據[{name:'abc',age:'xyz'},{name:'pqr',age:'xyz'}, ..]

通過上述設置,我已經使用 formArray 實現了反應式表單。

示例設置是在stackblitz 中創建的

這是我的表格:

<form [formGroup]="data_form">
  <table class="table table-border">
    <thead>
      <tr>
        <th>
          name
        </th>
        <th>
          age
        </th>
        <th><button class="btn btn-primary ">Save</button></th>
      </tr>
      <tr>
        <th *ngFor="let th of rowKeys">
          <ng-container *ngIf="th !=='isEditable'">
            <input type="text" formControlName="{{th}}" />
          </ng-container>
        </th>

        <th></th>
      </tr>
    </thead>
    <tbody formArrayName="persons">
      <ng-container *ngFor="let item of persons.controls;let j = index">
        <tr [formGroupName]="j">
          <ng-container *ngIf="!item.value.isEditable; else editable">
            <td>{{ item.value.name }}</td>
            <td>{{ item.value.age }}</td>
          </ng-container>
          <ng-template #editable>
            <td><input formControlName="name" /></td>
            <td><input formControlName="age" /></td>
          </ng-template>
          <td>
            <button (click)="toggleEdit(j)">
              {{ !item.value.isEditable ? "Edit": "Cancel"}}
            </button>
          </td>
        </tr>
      </ng-container>
    </tbody>
  </table>
</form>
<h2>
  {{data_form.status}}
</h2>

和:

import { Component } from "@angular/core";
import {
  FormArray,
  FormBuilder,
  FormControl,
  FormGroup,
  Validators
} from "@angular/forms";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  name = "Angular";
  constructor(private fb: FormBuilder) {}
  patterns = [
    /^[.\d]+$/,
    /^(yes|no)$/i,
    /^[a-zA-Z0-9 _/]+$/,
    /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/
  ];
  data = [
    {
      name: "Sachin",
      age: 27,
      isEditable: false
    },
    {
      name: "Gopal",
      age: 27,

      isEditable: false
    },
    {
      name: "Pankaj",
      age: 24,

      isEditable: false
    }
  ];
  rowKeys = Object.keys(this.data[0]);
  keys = [...new Set(this.data.map(item => Object.keys(item)).flat())];
  keyPattern = this.keys.map(item => ({
    key: item,
    pattern: this.patterns.find(pattern =>
      this.data.every(i => pattern.test(i[item]))
    )
  }));
  data_form = this.fb.group({
    persons: this.fb.array(
      this.data.map(item =>
        this.fb.group(
          this.keyPattern.reduce(
            (prev, { key, pattern }) => ({
              ...prev,
              [key]: [
                item[key],
                [Validators.required, Validators.pattern(pattern)]
              ]
            }),
            {}
          )
        )
      )
    )
  });
  get persons(): FormArray {
    return this.data_form.get("persons") as FormArray;
  }

  toggleEdit(j) {
    const currentEditStatus = this.persons.controls[j].get("isEditable").value;
    this.persons.controls[j].get("isEditable").setValue(!currentEditStatus);
  }
  ngOnInit(){
     this.rowKeys.forEach((num) => {
        if (num == "isEditable") return;
        const fc = new FormControl('');
        this.data_form.addControl(num, fc)
      });

      /**
       * How to filter formsArray ?
       */

      // this.data_form.get('cuisp').valueChanges.pipe(
      //   debounceTime(100),
      //   distinctUntilChanged(),
      // ).subscribe(val => {
      //   console.log(val)
      //   const result = this.persons.value.filter(res => {
      //     if (res['cuisp'] === val) {
      //       return res
      //     }
      //   });
      //   this.persons.patchValue(result)
      //   console.log(result)
      // });

  }

}

如何實現列級搜索,那么當我在 Name 列中搜索時,應顯示相應的名稱。

考慮以下使用反應式編程的方法。

步驟如下

  • 將所有輸入轉換為可觀察值
  • 設置一個Subject以用作過濾的觸發器
  • 使用來自rxjscombineLatest([...])運算符組合數據和主題

下面是一個工作代碼, this demo on stackblitz看到this demo on stackblitz

  constructor(private fb: FormBuilder) {}
  patterns = [
    /^[.\d]+$/,
    /^(yes|no)$/i,
    /^[a-zA-Z0-9 _/]+$/,
    /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/
  ];
  data$ = of([
    {
      name: "Sachin",
      age: 27,
      isEditable: false
    },
    {
      name: "Gopal",
      age: 27,

      isEditable: false
    },
    {
      name: "Pankaj",
      age: 24,

      isEditable: false
    }
  ]);
  filterStringSubject$ = new BehaviorSubject({});
  filterStringAction$ = this.filterStringSubject$.asObservable();
  filterString$ = this.filterStringAction$.pipe(
    map(stringObject =>
      Object.entries(stringObject).map(item => ({
        key: item[0],
        value: item[1]
      }))
    )
  );
  rowKeys$ = this.data$.pipe(
    map(data => Object.keys(data[0])),
    tap(rowKeys => {
      rowKeys.forEach(num => {
        if (num == "isEditable") return;
        this.filterStringSubject$.next({
          ...this.filterStringSubject$.value,
          [num]: ""
        });
      });
    })
  );
  keys$ = this.data$.pipe(
    map(data => [...new Set(data.map(item => Object.keys(item)).flat())])
  );
  keyPattern$ = combineLatest(this.keys$, this.data$).pipe(
    map(([keys, data]) => {
      return keys.map(item => ({
        key: item,
        pattern: this.patterns.find(pattern =>
          data.every(i => pattern.test(i[item]))
        )
      }));
    })
  );
  data_form: FormGroup;
  dataFiltered$ = combineLatest([this.data$, this.filterString$]).pipe(
    map(([data, filterString]) =>
      this.persons?.value.filter(item =>
        filterString.every(a => `${item[a.key]}`.includes(`${a.value}`))
      )
    )
  );
  dataForm$ = combineLatest([ this.data$,
    this.keyPattern$]).pipe(
tap(([data, keyPattern]) => { 
      this.data_form = this.fb.group({
        persons: this.fb.array(
          data.map(item =>
            this.fb.group(
              keyPattern.reduce(
                (prev, { key, pattern }) => ({
                  ...prev,
                  [key]: [
                    item[key],
                    [Validators.required, Validators.pattern(pattern)]
                  ]
                }),
                {}
              )
            )
          )
        )
      });
    }),
    )
  v$ = combineLatest([
    this.dataForm$,
    this.rowKeys$,
    this.filterString$,
    this.dataFiltered$
  ]).pipe(
    
    map(([, rowKeys, filterString, dataFiltered]) => ({
      rowKeys,
      filterString,
      dataFiltered
    }))
  );
  get persons(): FormArray {
    return this.data_form?.get("persons") as FormArray;
  }

  toggleEdit(j) {
    
    const currentEditStatus = this.persons.controls[j].get("isEditable").value;
    this.persons.controls[j].get("isEditable").setValue(!currentEditStatus);
  }
  filterBy(item, value) {
    this.filterStringSubject$.next({
      ...this.filterStringSubject$.value,
      [item]: value
    });
  }
  ngOnInit() { }

在你的 HTML

<form [formGroup]="data_form" *ngIf='v$ | async as v'>
    <table class="table table-border">

        <thead>
            <tr>
                <th>
                    name
                </th>
                <th>
                    age
                </th>
                <th><button class="btn btn-primary ">Save</button></th>
            </tr>
            <tr>
                <td *ngFor="let item of v.rowKeys">
                    <input *ngIf='item != "isEditable"' type="text"
          (input)="filterBy(item, $event.target.value)" />
        </td>

                <th></th>
            </tr>
        </thead>
        <tbody formArrayName="persons">
            <ng-container *ngFor="let item of v.dataFiltered;let j = index">
                <tr [formGroupName]="j">
                    <ng-container *ngIf="!persons.controls[j]?.get('isEditable').value; else editable">
                        <td>{{ item.name }}</td>
                        <td>{{ item.age }}</td>
                    </ng-container>
                    <ng-template #editable>
                        <td><input formControlName="name" /></td>
                        <td><input formControlName="age" /></td>
                    </ng-template>
                    <td>
                        <button (click)="toggleEdit(j)">
              {{ !persons.controls[j]?.get('isEditable').value ? "Edit": "Cancel"}}
            </button>
                    </td>
                </tr>
            </ng-container>
        </tbody>
    </table>
</form>
<h2>
    {{data_form?.status}}
</h2>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM