Angular 6 material table schematic: howto load data from remote?

Viewed 2461

I'm using the material table schematic and can't figure out how to

I think the important part is the connect() method in the datasource.ts file. this.data is just an array.

  /**
   * Connect this data source to the table. The table will only update when
   * the returned stream emits new items.
   * @returns A stream of the items to be rendered.
   */
  connect(): Observable<any[]> {
    // Combine everything that affects the rendered data into one update
    // stream for the data-table to consume.
    const dataMutations = [
      observableOf(this.data),
      this.paginator.page,
      this.sort.sortChange
    ];

    // Set the paginators length
    this.paginator.length = this.data.length;

    return merge(...dataMutations).pipe(
      map(() => {
        return this.getPagedData(this.getSortedData([...this.data]));
      })
    );
  }

How can I update this.data such that the table reflects the data? I only need to load the data once. I'd like filtering/sorting etc all to be done client side.

I found this guide, but it seems like overkill and was written before this schematic was created.

Isn't there some way to update this.data since its wrapped in an observableOf()?

4 Answers

I had this same problem. Changing dataSource.data = newData was not being detected by observableOf(this.data),.

Fixed it with the solution from this thread and shown in this example.

You have to replace that observableOf(this.data) with a this.datastream. So that connect method will end up looking like this:

connect(): Observable<DataTableItem[]> {
  const dataMutations = [
  this.dataStream,
  this.paginator.page,
  this.sort.sortChange

And in the beginning of the class, instead of declaring the variable data: TableItem[] = EXAMPLE_DATA;, you replace that with:

dataStream = new BehaviorSubject<DataTableItem[]>(EXAMPLE_DATA);
set data(v: DataTableItem[]) { this.dataStream.next(v); }
get data(): DataTableItem[] { return this.dataStream.value; }

Now you can call dataSource.data = newData anywhere with any new data and it just works. The datastream detects the change and the table gets updated. The problem was that observable of this.data was not really detecting any change. The datastream does.

The data source for the table needs to be an Observable.

As long as this.data is an array, there is no mechanism that will notify the data table when items are added, removed, or modified in the array.

After searching for tutorials I reverted back to the Official Example that turned out to be very intuitive.

I had a similar requirement to your question, Just load the data once and manage the manipulation at client-side.

However, I ended handling the manipulation at the server side.

Let me make an assumption, you have an endpoint that returns a list of objects.

/api/books/

1: Create a service with an http-call method to get your books endpoint(book.service.ts)

private booksUrl = "/api/books";
 _getBooks(bookId: number, filter='', sortOrder='asc', pageNumber=0, 
pageSize=3):Observable<Book[]>{
    return this.http.get(this.booksUrl, {
      params: new HttpParams()
          .set('bookId', _bookId.toString())
          .set('filter', filter)
          .set('sortOrder', sortOrder)
          .set('pageNumber', pageNumber.toString())
          .set('pageSize', pageSize.toString())
      }).pipe(
          tap(_ => this.log('fetched books')),
          catchError(this.handleError('getBooks', [])),
          map(res =>  {
        res["payload"] = res;
        return res["payload"];
      })
      );
    }

Define your Book class

export class Book{
    bookId: string;
    Title: string;
}

Then I created a Material-Table component using the data-table schematics.

I Edited the table-datasource as such

import { Book } from '../books';
import { BookService } from '../book.service';


/**
 * Data source for the Masterlist view. This class should
 * encapsulate all logic for fetching and manipulating the displayed data
 * (including sorting, pagination, and filtering).
 */
export class BooksDataSource implements DataSource<Book> {

  private booksSubject = new BehaviorSubject<Book[]>([]);
  private loadingSubject = new BehaviorSubject<boolean>(false);

  public loading$ = this.loadingSubject.asObservable();

  constructor(private bookService: BookService) {}

  /**
   * Connect this data source to the table. The table will only update when
   * the returned stream emits new items.
   * @returns A stream of the items to be rendered.
   */
  connect( collectionViewer:CollectionViewer): Observable<Book[]> {
    return this.booksSubject.asObservable();
  }

  /**
   *  Called when the table is being destroyed. Use this function, to clean up
   * any open connections or free any held resources that were set up during connect.
   */
  disconnect(collectionViewer: CollectionViewer): void {
    this.booksSubject.complete();
    this.loadingSubject.complete();
  }

  loadBooks(bookId: number, filter = '', sortDirection = 'asc', pageIndex = 0, pageSize = 3) {
    this.loadingSubject.next(true);
    this.bookService._getBook(bookId, filter, sortDirection,pageIndex, pageSize).pipe(
      catchError(e => throwError(e)),

      finalize(() => this.loadingSubject.next(false))
      )
      .subscribe(_books => this.booksSubject.next(_books));
}    
}

Finally, on the Books.Components.ts(your component)

export class BooksComponent implements OnInit {

  constructor(private bookService: BookService){}

  dataSource: BooksDataSource;

  /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
  displayedColumns = ['bookId', 'Title'];

  ngOnInit() {
    this.dataSource = new MasterlistDataSource(this.bookService);
    this.dataSource.loadBooks(1);
  }
}

From here you can map the datasource to your table.

The the Official Example continues to show how to implement sorting, pagination, filtering.

Happy coding :-)

Please try @matheo/datasource to setup your database service and your datasource in a clean way.

I've built a demo on StackBlitz and presented it details at:
https://medium.com/@matheo/reactive-datasource-for-angular-1d869b0155f6

I will try to get the time to provide an Schematic too with some boilerplate code as the official material one.
Looking forward for your opinion!

Related