Angular Material Table own datasource with filter method

你离开我真会死。 提交于 2019-12-06 13:23:31

@Pierre Mallet your filter solution works, but I don't really know where to call

this.filter$.next({criteria: <theCriteria>, searchTerm: <theTerm>});

and I did a little change because of the angular material select.

My actual code

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/filter';
import 'rxjs/add/observable/combineLatest';
import { DataSource } from '@angular/cdk/collections';
import { UserService } from '../shared/service/user.service';
import { User } from '../shared/user';
import { TransponderFormComponent } from '../transponder-form/transponder-form.component';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

@Component({
  selector: 'app-transponder-list',
  templateUrl: './transponder-list.component.html',
  styleUrls: ['./transponder-list.component.scss']
})

export class TransponderListComponent implements OnInit {

  public users: User[];
  public displayedColumns = ['name', 'adress', 'city', 'email', 'telephon', 'birthday'];
  public dataSource;
  public selectedCriteria: String = '';

  public searchCriteria = [
    { value: 'name', view: 'Name' },
    { value: 'city', view: 'City' },
    { value: 'adress', view: 'Adress' },
    { value: 'email', view: 'Email' },
    { value: 'telephon', view: 'Telephon' },
    { value: 'birthday', view: 'Birthday' }
  ];

  constructor(private userService: UserService) { }

  ngOnInit() {
    this.dataSource = new MyDataSource(this.userService);
  }

  // get data from material select
  public setSearchCriteria() { }

  public filter(data: any) {
    this.dataSource.filter(data.searchTerm, this.selectedColumn);
  }
}

export class MyDataSource extends DataSource<any> {

  private users: Observable<User[]>;
  private filter$ = new BehaviorSubject({ criteria: null, searchTerm: null });

  constructor(private userService: UserService) {
    super();
  }

  connect(): Observable<User[]> {
    this.users = this.userService.getAll();
    return Observable.combineLatest(this.userService.getAll(), this.filter$)
      .map(latestValues => {
         const [users, filterData] = latestValues; // change
         if (!filterData.criteria || !filterData.searchTerm) {
           return users;
         }
         return users.filter(user => {
           return user[filterData.criteria].toLocaleLowerCase().includes(filterData.searchTerm.toLocaleLowerCase());
         });
       });
  }

  disconnect() { }

  filter(searchTerm: String, criteria: String) {
    if (searchTerm !== '') {
      // there is a tern to search
      if ((criteria !== undefined) && (criteria !== '')) {
        console.log('Search with term and criteria');
        this.filterWithCriteria(searchTerm, criteria);
      }
    }
  }

  filterWithCriteria(searchTerm: String, criteria: any): any {
    return this.users
      .map((users: User[]) => {
        return users.filter(user => {
          return user[criteria].toLocaleLowerCase().includes(searchTerm.toLocaleLowerCase());
        });
      })
      .subscribe(
        users => console.log(users),
        err => console.error(err),
        () => console.log('Streaming is over')
      );
  }
}

Thanks for your help.

Best regards.

There are (I think) two mistakes in your example :

1/ it seems your userService.getAll() return an Observable to an observable wich emit an array of users.

So when you do return this.users.filter(element ... element is an array of User, not a User

then the good filtering chain should be

filterWithCriteria(filterData: any): any {
    return this.users
      .map((users: User[] => {
          return users.filter(user => {
              return user[filterData.criteria].toLocaleLowerCase().includes(filterData.searchTerm.toLocaleLowerCase());
          });  
      })
      .subscribe(
        users => console.log(users),
        err => console.error(err),
        () => console.log('Streaming is over')
      );
   }

2/ The connect of your DataSource should be an Observable emitting the Users you want to display. So the connect method need to return an Observable which will emit new data when the filter is changed (or list of Users is changed). We lack information on how you filter is implemented but here is an example of implementation

export class MyDataSource extends DataSource<User[]> {

        private users: Observable<User[]>;
        private filter$ = new BehaviorSubject({criteria: null, searchTerm: null});
        /*
            Every time your filter change you have to call
            this.filter$.next({criteria: <theCriteria>, searchTerm: <theTerm>});
        */

        constructor(private userService: UserService) {
            super();
        }

        connect(): Observable<User[]> {
            return Observable.combineLatest(this.userService.getAll(), this.filter$)
                .map(latestValues => {
                    // every time filter$ or the observable from userService.getAll() emit a new data
                    const [users, filterData] = latestValues;
                    if (!filterData.criteria || !filterData.searchTerm) return users;

                    return users.filter(user => {
                        return user[filterData.criteria].toLocaleLowerCase().includes(filterData.searchTerm.toLocaleLowerCase());
                    });
                });
        }

        disconnect() {}
    }

Note: the best way to handle this could be to have your filter$ Subject in your wrapping component, and pass it in the constructor of your DataSource.

Hope it helps !

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!