Angular Data Tables for jQuery DataTables Developers

A Simple Start

I'm skipping the "Getting Started" tutorial with a static array, because it's already there, and (as that tutorial mentions), there is virtually no "real world" use for a table built from a static array in the front end. I'm instead going to move on to what amounts to the previous practice of "AJAX-ing in" data, very much like this function from jQuery DataTables, which looks like this on the "old site":
function initDeptTable () {
	var table = $('#employee-dept').DataTable({
		"ajax" : {
			"url" : '../main/php/serverHTML.php?type=JSON&content=employee-dept',
	        "cache": false,
            "contentType": "application/json",
    },
I've based this update example largely based upon the "Table retrieving data through HTTP" example here, with a large number of "tweaks" because I had the ability to use multiple services/classes/interfaces/etc, and my own API.
In that example, there are two simple interfaces, one for the collection of items and a count of those items and another for the items themselves. This is a solid practice (and as you might guess it's typically best to do the count on the back end if you have control over the API). The only change I made was to break these up into multiple files:
export interface Department {
  DepartmentName: string;
  DepartmentNumber: string;
}
import { Department } from './department.model';

export interface DepartmentApi {
    items: Department[];
    total_count: number;
}
As compared to jQuery DataTables, this might seem a bit complicated (and there is a little more work so far), but keep in mind we're getting dynamic table content and this is our means of reformatting the JSON into the specific format we need, removing the need to reformat it on the back-end or with custom JavaScript. I had to use this PHP to reformat my previous JSON like this for jQuery DataTables. With more control over each step of building the table, we also gain more flexibility. For example, I'm going to integrate some of this tutorial (note it's for Angular 6, but we can still use most of it). Specifically, the concept of using an observable for our data instead of MatTableDataSource (here's why). Here's a service that provides that Observable:
import { Injectable } from '@angular/core';
import { Observable, throwError } from 'rxjs';
import { HttpClient, HttpParams, HttpErrorResponse } from '@angular/common/http';
import { catchError, retry } from 'rxjs/operators';

import { DepartmentApi } from './shared/department-api.model';

@Injectable({
  providedIn: 'root'
})
export class DepartmentsService {

  constructor(private http: HttpClient) { }

  DepartmentUrl = 'https://api.benfrog.net/departments';
  
  

  getDepartments(sortDirection: string, sortParameter: string ): Observable<DepartmentApi> {
    sortDirection = sortDirection.trim();
    sortParameter = sortParameter.trim();

    let httpParams = new HttpParams({ fromObject: { sortDirection: sortDirection, sortBy: sortParameter } });

    return this.http.get<DepartmentApi>(this.DepartmentUrl, {params: httpParams})
    .pipe(
      retry(3), // retry a failed request up to 3 times
      catchError(this.handleError) // then handle the error
    );
  }

  private handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error('An error occurred:', error.error.message);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong,
      console.error(
        'Backend returned code error.status, ' +
        'body was: error.error ');
    }
    // return an observable with a user-facing error message
    return throwError(
      'Something bad happened; please try again later.');
  };

}
What we are doing here is providing an Observable that calls the HttpClient's http.get() function, expects a return as a "DepartmentApi" object (the array of Department objects and the count of those object), and implements some simple error handling:
return this.http.get<DepartmentApi>(this.DepartmentUrl, {params: httpParams})
    .pipe(
      retry(3), // retry a failed request up to 3 times
      catchError(this.handleError) // then handle the error
    );
  }
In the Department Table Component's ngOnInit function (which works much like the oft-used jQuery $(document).ready() function), the constructor requires its own private instance of HttpClient, then passes it along to an instance of the above departmentService, which makes use of it to get our API data (the entire component is here):
this.departmentsService = new DepartmentsService(this.http);
     merge(this.sort.sortChange)
      .pipe(
          startWith({}),
          switchMap(() => {
            this.isLoadingResults = true;
            return this.departmentsService!.getDepartments(this.sort.direction, this.sort.active)
          }),
          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);
A large amount just happened in a small amount of code, which is a great feature of Angular provided it does not contribute to confusion on the part of the developer. Let's break it down (and it's also a great idea for this or any other data flow to make use of the excellent stack trace tools we have now such as the the Visual Studio Code Debugger for Firefox or Chrome to inspect your variables at every stage of this process). First, we have an (admittedly unnecessary) RxJs Merge (we're actually merging the matSortChange event with nothing, but can add events if necessary), then using the RxJS Observable.pipe function (as documented here).
Step by step, the pipe uses the literally-named RxJS StartWith() function to begin with an empty observable, then the RxJs switchMap function (there's a deep dive into how switchMap works here) essentially switches around that empty observable with the output of departmentsService.getDepartments() when we start to get data from that service. The map function then removes a cosmetic loading flag, sets the resultsLength variable equal to data.length, and finally returns the array of items (a Department[] object). Because we subscribe to this observable, whenever the department data updates our table will update. If this sounds familiar, it's likely because it shares that advantage with jQuery AJAX (an observable is actually much more sophisticated and flexible, in that it does not require a direct call to update our table data).
The completed sample table's HTML is here. I've made very few changes from the above examples, and we finally get this (very simple) table:
d001Marketing
d002Finance
d003Human Resources
d004Production
d005Development
d006Quality Management
d007Sales
d008Research
d009Customer Service
At this point, anyone could be forgiven for asking if all of that was "worth it", particularly since we still don't have base elements from jQuery DataTables such as search and pagination. However, we're going to move on to showcase the power and flexibility of Angular to implement those features (with newfound flexibility) in the next example.