About a month ago, I created my first Angular Material Table. Things went well and the implementation of filtering, sorting, and paging was as described by the docs. Several tables into my project , I had to shape my table input data. Probably due to inexperience with Observables and/or the use of EventEmitters, I began to struggle. The docs have an async http example, but it didn’t address shaping and sharing table data between components.
Searches for examples showed frustration among some. Often the input data issue was obscured by Material Table features that were unrelated to the problem. Also, I didn’t find any recommended practice for managing table input data, so I created this StackBlitz to see if I could better understand options and preferences.
<table mat-table> directive creates a Material Design styled data-table. Directive mat-table has a dataSource input which can be an array, an Observable array, or a DataSource instance. A
<table mat-table [dataSource] = simple_array> will produce a static material-styled table. Changes to the simple array will not be propagated to the table. If
<table mat-table [dataSource] = Observable<any>, then changes to the observable array are propagated to the table. Finally, if
<table mat-table [dataSource] = DataSource instance>, then changes, sorting, filtering, pagination, etc. are encapsulated in the class instance for easy access. This instance can be created by passing an array or Observable array as follows:
instance = new MatTableDataSource(array).
The StackBlitz shows the creation of Material Tables using:
- static data – simple array,
- a service property – DataSource Instance
- a service Observable,
- a service Observable in a child component – DataSource Instance,
- a child component emitting to a function – DataSource Instance.
Get Started – Open the StackBlitz to follow along
I used the Material Table “Basic use of” example at https://material.angular.io/components/table/examples as a starting point. Other than the change to the
[dataSource] input, the sample html was not modified.
- Data are provided by an array const directly in app.component. Table No. 1 shows this implementation. Other than moving the data model interface to an external class, nothing differs from the “Basic use of” sample.
- In most applications, data come from a service. I created
ElementServicein dataService.ts with a
listElementsproperty and a
getElements()method. Table No. 2 uses the property accessor to create the table. As with Table No.1, it is a synchronous implementation.
- Typically, data from a service are async using http. I mimicked this behavior with an Obervable.delay.
timedelayis a variable to populate each async table sequentially. Table No. 3 subscribes to the Observable created in the service with a two second delay. On refresh, you will see this table populate after two seconds.
Tables No. 4a and 4b are examples of shaping the data input in a child component. You can pass either the observable function or the response of subscribing to the observable. I chose to pass the function and subscribed to it in the child component. The function is passed in the app.component.html as
[listElementChild] = ‘elsService’. In VS Code, hovering over these objects provides the component name.
[listElementChild], a service function, is passed to the child.component using the @Input() decorator. In
ngOnInit, the observable is subscribed to and then new objects/records are added to the observable array.
Table No 4a. uses the child.component.html and subscribes to the passed observable function. A new element (Beryllium) is pushed onto the response and that response is set and the mat-table’s dataSource.
Table No. 4b adds a fifth element (Boron) and creates a
DataSource instanceas the table’s input and uses the child.component.html.
Table No.5 creates the shaped observable and emits it via the Output() decorator to the parent. The event is being watched in app.component.html by
(dsEvent) = 'onChildDS($event)'. When
onChildDS()method creates a new