1

我想实现高度动态的表格,并应用列级过滤器和可编辑的行,并对表格的每个单元格应用验证。

我已经实现了带有可编辑行和动态验证的动态表格显示。但是在列级过滤器上苦苦挣扎。

我的问题陈述:

  • 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>

和 ts:

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)
      // });

  }

}

如何实现列级搜索,所以当我在名称列中搜索时,应该显示相应的名称。

4

1 回答 1

1

考虑以下使用反应式编程的方法。

步骤如下

  • 将所有输入转换为 observables
  • 设置一个Subject用作过滤的触发器
  • combineLatest([...])使用来自的运算符组合数据和主题rxjs

下面是一个工作代码,见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>
于 2020-10-26T14:15:47.537 回答