Tìm kiếm trang web

typescript - Angular Material - Lọc bảng bằng nhiều bộ lọc và nhiều giá trị lọc cùng một lúc


Tôi đang cố gắng để có thể sử dụng hai bộ lọc khác nhau cho một bảng các đối tượng. Bộ lọc đầu tiên là bộ lọc dựa trên văn bản/nhập liệu bình thường và hoạt động như mong đợi.

Cái thứ hai mà tôi đang cố gắng làm cho hoạt động là một hàng các hộp kiểm được gán nhãn 'Cấp 1', 'Cấp 2', v.v. Khi đánh dấu một hộp kiểm, tôi muốn lọc theo cột 'cấp' tất cả các hộp kiểm đang được chọn hiện tại. Lý tưởng nhất là người dùng có thể lọc bảng theo cả văn bản và lựa chọn cấp độ.

Tôi đã đọc về việc sử dụng filterPredicate và đã thử sử dụng điều này như một mẫu nhưng có lẽ tôi đã bỏ lỡ điều gì đó.

Đoạn mã hiện tại:

HTML :

//Input for text filter
<mat-form-field >
  <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter...">
</mat-form-field>

...

//Checkboxes for level filter
<div fxLayout="row" fxLayoutAlign="space-between center" class="margin-1">
    <mat-checkbox (change)="customFilterPredicate()" [(ngModel)]="level.active" *ngFor="let level of levelsToShow">{{level.level}}</mat-checkbox>
</div>

TS

ngOnInit() {
     this.dataSource.filterPredicate = this.customFilterPredicate();
  }

...

applyFilter(filterValue: string) {
    this.dataSource.filter = filterValue.trim().toLowerCase();
  }

customFilterPredicate() {
    const myFilterPredicate = (data): boolean => {
      return this.levelsToShow.some(data['level'].toString().trim());
    };
    return myFilterPredicate;
  }

Cập nhật

Cho đến nay tôi đã có thể đạt được một số tiến bộ nhờ Fabian Küng nhưng vẫn chưa hoàn thành. Để làm rõ, tôi hy vọng bộ lọc văn bản có thể tìm kiếm qua nhiều cột ('castingTime', 'duration', v.v.) và các hộp kiểm chỉ lọc theo cấp độ.

Hiện tại khi tôi nhấp vào một hộp kiểm thì tôi nhận được lỗi này: 'Không thể đọc thuộc tính 'toLowerCase' của undefined' mà trỏ đến dòng mã này:

return data['level'].toString().trim().toLowerCase().indexOf(searchString.name.toLowerCase()) !== -1 &&

nhưng khi tôi dùng console log dữ liệu, tôi có thể thấy rằng nó có một thuộc tính level, vì vậy tôi không thấy mình sai ở đâu.

Đây là các đoạn mã liên quan:

    HTML:
    
        <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter..." [formControl]="textFilter">
        
        <mat-checkbox (change)="updateFilter()" [(ngModel)]="level.active" *ngFor="let level of levelsToShow">{{level.name}}</mat-checkbox>
    
    TS:
    
        levelsToShow: Level[] = [
            { level: '1', active: false, name: 'Level 1' },
            { level: '2', active: false, name: 'Level 2' },
            { level: '3', active: false, name: 'Level 3' },
            { level: '4', active: false, name: 'Level 4' },
            { level: '5', active: false, name: 'Level 5' },
            { level: '6', active: false, name: 'Level 6' },
            { level: '7', active: false, name: 'Level 7' },
            { level: '8', active: false, name: 'Level 8' },
            { level: '9', active: false, name: 'Level 9' }
          ];
        
          levelFilter = new FormControl();
          textFilter = new FormControl();
          globalFilter = '';
        
          filteredValues = {
            level: '', 
            text: '', 
          };

ngOnInit() {
    ...

    this.textFilter.valueChanges.subscribe((textFilterValue) => {
      this.filteredValues['text'] = textFilterValue;
      this.dataSource.filter = JSON.stringify(this.filteredValues);
    });

    this.dataSource.filterPredicate = this.customFilterPredicate();
  }

  customFilterPredicate() {
    const myFilterPredicate = (data: Spell, filter: string): boolean => {
      var globalMatch = !this.globalFilter;

      if (this.globalFilter) {
        // search all text fields
        globalMatch = data['spellName'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
                      data['level'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
                      data['castingTime'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
                      data['distance'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
                      data['details'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
                      data['duration'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
                      data['school'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
                      data['effect'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1;
      }

      if (!globalMatch) {
        return;
      }

      let searchString = JSON.parse(filter);

      return data['level'].toString().trim().toLowerCase().indexOf(searchString.name.toLowerCase()) !== -1 &&
        (this.levelsToShow.filter(level => !level.active).length === this.levelsToShow.length ||
          this.levelsToShow.filter(level => level.active).some(level => level.level === data.level.toString()));
    }
    return myFilterPredicate;
  }

  updateFilter() {
    this.dataSource.filter = JSON.stringify(this.filteredValues);
  }

Cập nhật II

Như đã yêu cầu, đây là một ví dụ về một trong những hàng của bảng: (đây là cho một bảng phép thuật d&d)

{
castingTime: "1 half action"
distance: "Short range attack"
duration: "1 round per Casting Level"
effect: "Once per round, you may take a half action to launch an arrow of acid from your palm, generating a new Spellcasting result to see if you hit.Each arrow inflicts 1d6 acid damage.↵  "
index: 0
level: "4"
school: "Creation"
spellName: "ACID ARROW"
}

Cập nhật cuối

Đây là kết quả tôi đạt được khi có người gặp khó khăn. Cảm ơn Fabian Küng vì đã tìm ra tất cả những điều này! Điều kỳ lạ duy nhất cần lưu ý là tôi đã sử dụng giá trị thực tế của kiểu nhập văn bản "textFilter" vì chuyển đổi các giá trị văn bản FilterValues thành một chuỗi rồi phân tích cú pháp (bộ lọc chỉ chấp nhận các chuỗi từ những gì tôi đã thấy) liên tục đưa ra lỗi "Lỗi JSON, giá trị không mong muốn ở mức 0" và theo những gì tôi có thể thấy, điều này sẽ hoạt động tốt NGOẠI TRỪ bây giờ tôi phải tìm cách kiểm soát tốc độ bộ lọc.

customFilterPredicate() {
    const myFilterPredicate = (data: Spell, filter: string): boolean => {
      var globalMatch = !this.globalFilter;

      if (this.globalFilter) {
        // search all text fields
        globalMatch = data['spellName'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
                      data['level'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
                      data['castingTime'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
                      data['distance'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
                      data['details'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
                      data['duration'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
                      data['school'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
                      data['effect'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1;
      }

      if (!globalMatch) {
        return;
      }

      return data.spellName.toString().trim().toLowerCase().indexOf(this.textFilter.value) !== -1 &&
        (this.levelsToShow.filter(level => !level.active).length === this.levelsToShow.length ||
          this.levelsToShow.filter(level => level.active).some(level => level.level === data.level.toString()));
    }
    return myFilterPredicate;
  }

Câu trả lời #1

Bạn đã đi đúng hướng khi thiết lập các hộp kiểm và sau đó sử dụng mô hình của các hộp kiểm để lọc trong hàm customFilterPredicate.

Tôi đã lấy stackblitz được liên kết và chỉnh sửa nó để hoạt động với các hộp kiểm của bạn, kiểm tra nó ở đây.

Trong mẫu, bạn có các hộp kiểm như bạn đã thiết lập:

<mat-checkbox (change)="updateFilter()" [(ngModel)]="level.active" *ngFor="let level of levelsToShow">{{level.name}}</mat-checkbox>

Trong thành phần, tôi đã thêm một giao diện Level:

export interface Level {
  active: boolean;
  name: string;
  level: number;
}

và một số dữ liệu:

levelsToShow: Level[] = [
  { level: 1, active: false, name: 'Level 1' },
  { level: 2, active: false, name: 'Level 2' },
  { level: 3, active: false, name: 'Level 3' },
];

Và phần quan trọng nhất nằm trong hàm customFilterPredicate. Nó thực hiện việc lọc thuộc tính name trong điều kiện đầu tiên và trong điều kiện thứ hai, nó kiểm tra xem tất cả các cấp độ có được đặt là false hay không, trong trường hợp này chúng ta trả về true vì tôi giả định bạn muốn xem tất cả các cấp độ nếu không có hộp kiểm nào được chọn. Nếu một cấp độ đang hoạt động, chúng ta lọc theo cấp độ đó, hoặc nhiều cấp độ nếu có nhiều mức được chọn.

return data.name.toString().trim().toLowerCase().indexOf(searchString.name.toLowerCase()) !== -1 &&
        (this.levelsToShow.filter(level => !level.active).length === this.levelsToShow.length ||
         this.levelsToShow.filter(level => level.active).some(level => level.level === data.level));

Một phần nhỏ nhưng quan trọng là thực sự kích hoạt việc lọc khi bạn chọn/bỏ chọn một hộp kiểm. Điều này có thể được thực hiện bằng cách chỉ cần đặt lại filter của nguồn dữ liệu và được gọi mỗi khi giá trị của hộp kiểm thay đổi:

updateFilter() {
  this.dataSource.filter = JSON.stringify(this.filteredValues);
}

Câu trả lời #2

CÁCH DỄ DÀNG HƠN

Sử dụng FilterPredicate trong datasource để đặt nhiều biểu thức

Sử dụng Nhóm Biểu mẫu để gán các điều khiển sẽ gửi filterValue

<form [formGroup]="filterForm">

  <mat-form-field>
    <input matInput placeholder="Model" formControlName="model" />
  </mat-form-field>

  <mat-form-field>
    <input matInput placeholder="Type" formControlName="modelType" />
  </mat-form-field>

  <mat-form-field appearance="fill">
    <mat-label>Status</mat-label>
    <mat-select formControlName="status">
      <mat-option value=""></mat-option>
      <mat-option value="Active">Active</mat-option>
      <mat-option value="InActive">InActive</mat-option>
    </mat-select>
  </mat-form-field>

#TypeScript Chỉ định biểu thức filterPredicate

// Filter Predicate Expression to perform Multiple KeyWord Search
    this.dataSource.filterPredicate = ((data: DeviceInventory, filter) => {          
      const a = !filter.model || data.modelCode.trim().toLowerCase().includes(filter.model);
      const b = !filter.modelType || data.modelType.trim().toLowerCase().includes(filter.modelType);
      return a && b;
    }) as (DeviceInventory, string) => boolean;

Đăng ký các thay đổi giá trị của FomrGroup để lắng nghe filterValues

this.filterForm.valueChanges.subscribe(value => {
  this.dataSource.filter = value;
  console.log(value);
});

}

Bài viết liên quan