Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mat-sort with rxjs is not working properly

I have the problem with implement mat-sort in mat-table, when source was created from observers stream.

Simply implementation it via documentation by:

ngAfterViewInit() {
    this.dataSource.sort = this.sort;
}

is not working properly - always will be sorted only 5 rows on my table.

I think, my problem is with the proper use it with rxjs connection.

Unfortunately, after checking another questions/documentation I can't find any idea.

I generated data source from two observer stream. Also I used for it BehaviourSubject (for initial values), combineLatest and switch map. Table was created properly and working perfectly.

Also when I added filter (according to angular material design documentation) is working properly. But mat-sort... not (only 5 first rows).

    ngOnInit() {
            this.filters = itemFilters;
            this.idSubject = new BehaviorSubject(this.filters[0]);
            Observable.combineLatest(this.name, this.selectedFilter)
                .do(_ => this.items = null)
                .switchMap(([name, filterIndex]: [Name | null, number]) => {
                    const item = this.filters[filterIndex];
                    this.namesSubject.next(item.display);
                    return this.itemService.getItems(name);
                })
                .subscribe(this.setItems.bind(this), this.setError.bind(this));
        }

Also I tried with Observable.zip - but I think it is also not my case. Any idea/advice will be very valuable.

I think, I should subscribe sorting method to observable streams. That same problem I have with pagination. Sometimes works, sometimes not.

like image 935
profiler Avatar asked Aug 20 '18 11:08

profiler


1 Answers

Your question code looks like the example from mat-tables that display data from HTTP calls: https://stackblitz.com/angular/rmoxkmpkkyj?file=app%2Ftable-http-example.ts

I think the implementation can be simplified by handling the pagination and sorting events separately.

Please take a look at this example I build that refreshes the data on each event: https://stackblitz.com/edit/angular-material-mat-table-sort-merge-streams?file=src%2Fapp%2Fmy-books%2Fmy-books.component.ts

The Event handlers

ngOnInit() {

    // default data 
    this.refresh(this.getDefaultOptions());

    this.sort.sortChange.subscribe((sort: Sort) => {
      console.log('sortChange', this.sort.active);
      this.paginator.pageIndex = 0;
      this.refresh(this.getCurrentOptions());
    });

    this.paginator.page.subscribe((page: PageEvent) => {
      console.log('paginator ', page);
      this.refresh(this.getCurrentOptions());
    });

}

The method to get the current view options

getCurrentOptions() {
    const options: ViewOptions = {
      sortField: this.sort.active,
      sortDirection: this.sort.direction,
      page: this.paginator.pageIndex,
      pageSize: this.paginator.pageSize
    };

    return options;
  }

An example of how to merge multiple streams

  findBooks(options: ViewOptions): Observable<BooksResponse> {

    console.log('findBooks', options);

    // retrieve multiple streams
    const multipleStreams = this.mockMultipleStreams();

    // sort and slice result
    const sortedAndSliced = multipleStreams.pipe(
      tap(items => {
        items = items.sort((a, b) => {
          const sortOrder = options.sortDirection === 'asc' ? -1 : 1;
          const valueA = a[options.sortField];
          const valueB = b[options.sortField];

          var result = (valueA < valueB) ? -1 : (valueA > valueB) ? 1 : 0;
          return result * sortOrder;
        });
      }),
      tap((items: Book[]) => {
        const start = options.page * options.pageSize;
        const end = start + options.pageSize;
        items = items.slice(start, end);
      })
    );

    // wrap in the response object
    const bookResponse = sortedAndSliced.pipe(
      map((items: Book[]) => {
        const response: BooksResponse = {
          items: items,
          total: this.fakeDataLength
        };
        return response;
      })
    );

    return bookResponse;

  }

  mockMultipleStreams(): Observable<Book[]> {
    const third = this.fakeDataLength / 3;

    // merge all the streams together
    const allTheBooks: Observable<[Book[], Book[], Book[]]> = zip(
      of(this.mockBooks('Magazine', third)),
      of(this.mockBooks('Books', third)),
      of(this.mockBooks('Newspaper', third))
    );

    // flatten the data 
    const result = allTheBooks
      .pipe(map((items) => {
        let result: Book[] = [];
        items.forEach(books => {
          books.forEach(book => { result.push(book) })
        });
        return result;
      }));

      return result;
  }

See the full code here: https://stackblitz.com/edit/angular-material-mat-table-sort-merge-streams?file=src%2Fapp%2Fbooks.service.ts

like image 113
hamilton.lima Avatar answered Nov 06 '22 05:11

hamilton.lima