Angular Data Tables for jQuery DataTables Developers

Let's Make it More Interesting

We just created a very simple table, but how do we make a table in Angular Material that actually has the features we've come to depend on in jQuery Datatables? There is no one "right" answer, which is actually pretty great (a large part of Angular's power lies in its flexibility). That being said, let's start by mimicking the "default" functionality of jQuery DataTables with a simple "search all" at the top of the table and pagination at the bottom (with a more diverse set of data). We're going to mainly use a combination of this example this tutorial, however we are going to use our API data (unlike the first example), are going to modify the tutorial example to use a different means to get our data upon search (largely to show we can) and finally are going to use Angular 7 (the tutorial uses Angular 6, and the largest change is mapping our data return and making use of switchMap in the Observable we subscribe to, as in the example above).
First, let's add our search element following largely the tutorial's example as the example from the Angular Material Documentation "works" without issue, but lacks a great deal of flexibility due to the fact that it embeds the keyup() function inside of the HTML:
<mat-form-field>
  <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
</mat-form-field>
The approach used in the tutorial is much more flexible and (as mentioned there) allows us to prevent overwhelming our (or another person's) API with a ton of requests on each user keyup/keydown event. It (and this example) first begins with a native input element:
fromEvent(this.input.nativeElement, 'keyup')
      .pipe(
        debounceTime(200),
        distinctUntilChanged(),
Then (after starting with blank content in that filter, shown in the complete component here) we can move along to determining if a user "really wants" to search by including an RxJS debounceTime (which you can of course adjust depending on user experience and API rate) and RxJS distinctUntilChange (which prevents hits on the API when there is no change in the filter criteria):
fromEvent(this.input.nativeElement, 'keyup')
      .pipe(
        debounceTime(200),
        distinctUntilChanged(),
Now whenever the content of the "Search" field changes, it will be passed to the employee service's getEmployees function as a parameter, and in turn passed along to the API:
getEmployees(sortDirection: string, sortParameter: string, filter: string, start: number, end: number): Observable<EmployeeApi> {
        sortDirection = sortDirection.trim();
        sortParameter = sortParameter.trim();
        filter = filter.trim();

        //toString is necessary here because httpClient does not support anything but strings 
        let httpParams = new HttpParams({
          fromObject: {
              sortDirection: sortDirection, sortParameter: sortParameter, filter: filter,
              start: start.toString(), end: end.toString()
          }
        });
You likely noticed that function and service overall are not much different than the Departments Service and its getDepartments function. In fact, we've just added a few parameters. Those of us migrating from jQuery DataTables Server-Side processing might already spot an advantage: I've used a standard and conventional Controller in the API that is really not customized for this table at all. In fact, even if I did not have the ability to customize this API, it would be reasonable to expect to pass in a filter and get results. Server-side jQuery DataTables has rather strict requirements for the server-side script, thus making it difficult to implement if you lack access to an API, or want to reuse an endpoint for another application.
There is also a very solid implementation of pagination in that tutorial (with a flexible number of rows per page), and it's not tough at all to add it to our table and make it function largely like the familiar DataTables component. It depends on resultsLength, which is easily obtained from the "count" in the "API" interface. First, we'll add a "mat-paginator" element to the bottom of the table:
<mat-paginator [length]="resultsLength" [pageSize]="10" [pageSizeOptions]="[10, 20, 50]"></mat-paginator>
Then, back in the employee table component, we'll make use of the RxJS Merge function to emit the values of a change in the sort parameter and (now) the pagination element (note that we are also resetting pagination to page zero when sorting changes):
 this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0);
    this.sort.direction = 'asc';
    this.sort.active = 'emp_no';
    this.paginator.pageIndex = 0;
    this.paginator.pageSize = 10;
    this.input.nativeElement.value = '';
    
    this.sort.sortChange.subscribe();
    this.employeeService = new EmployeeService(this.http);
    merge(this.sort.sortChange, this.paginator.page)
      .pipe(
        startWith({}),
        switchMap(() => {
          this.isLoadingResults = true;
          return this.employeeService!.getEmployees(
              this.sort.direction,
              this.sort.active,
              this.input.nativeElement.value,
              this.paginator.pageIndex,
              this.paginator.pageSize)
        }),
        map(data => {
          // Flip flag to show that loading has finished.
          this.isLoadingResults = false;
          this.resultsLength = data.total_count;
          return data.items;
        }),
      ).subscribe(data => this.data = data);
  }
Now whenever the user changes the pagination, filter, or (like the first, very simple table) sort parameter, we make another request to the API for the data. This now looks, feels, and functions very much like a jQuery DataTable, as you'll notice:
10001GeorgiFacelloSeptember 2, 1953June 26, 1986
10002BezalelSimmelJune 2, 1964August 3, 1996
10003PartoBamfordDecember 3, 1959December 3, 1995
10004ChirstianKoblickMay 1, 1954December 1, 1986
10005KyoichiMaliniakJanuary 21, 1955September 12, 1989
10006AnnekePreusigApril 20, 1953August 5, 1990
10007TzvetanZielinskiMay 23, 1957February 10, 1989
10008SaniyaKalloufiFebruary 19, 1958March 11, 1998
10009SumantPeacApril 19, 1952February 18, 1985
10010DuangkaewPiveteauJune 1, 1963November 24, 1996
Items per page:
1 - 10 of 331603
If you take a look at the complete HTML of the table component, you'll note that I've added some other small "tweaks", such as disabling sorting on the two date fields (simply because sorting by date is painfully slow on my very small database server). These changes are very easy, and even fairly large visual and functional modifications aren't difficult, as you'll notice from the complete set of examples on the Angular Material Documentation page (which is frequently expanded) now that you have a functioning table. Keep an eye on it and this site for more examples of how to versatile Data Tables really can be once you accept the fact DataTables can be two words.