what makes ANGULAR MATERIAL tables super cool

We have been doing a PoC recently to find the best grid structure for front end data display of our business application and the participants were ag grid , ng2 tables and angular material tables.

My task on the above mentioned TOC was to fetch data from our back office and display them on an Angular material table in the front end but doing this was bit time consuming as there was hardly any documentation on angular material as it is relatively new. Therefore I had to stick with the official documentation of angular material and few plunker examples due to the unavailability of resources on this subject area.

I found it quite hard to follow the official documentation and change the examples given such that they suit our application. Therefore thought of writing this article describe the md table examples given in the official page so that it will give you a clean cut idea about how angular material tables work.

Before you begin you will have to install angular material to your application

npm install --save @angular/material 

this is the official material package for angular

npm install --save @angular/animations

And certain material components need access to animations therefore install animations package along with material.

Follow this for more info on getting started with angular material https://material.angular.io/guide/getting-started

The as the second step import the packages we just installed

import {BrowserAnimationsModule} from '@angular/platform-browser/animations';

import {MdTableModule} from '@angular/material';

import packages and necessary modules

lets take the code piece by piece.you will find the complete code here https://material.angular.io/components/table/overview

import {Component} from '@angular/core';import {DataSource} from '@angular/cdk';import {BehaviorSubject} from 'rxjs/BehaviorSubject';import {Observable} from 'rxjs/Observable';import 'rxjs/add/operator/startWith';import 'rxjs/add/observable/merge';import 'rxjs/add/operator/map';

import the necessary packages you require. Make sure you install angular cdk while you are installing angular material.

Before proceeding it is better to mention what is a data source what is an observable and what is a behavior subject and also rxjs.

RXJS is a hot topic in frond end development these days .It is a javascript library for reactive programming ( Reactive programming is just a way of building software applications. Essentially, your software is built to “react” to changes that happen (like click events, data being fetched, etc)) using Observables, to make it easier to compose asynchronous or callback-based code.

cdk is imported because md-tables are built on top of it. Cdk tables are the foundation to md tables.

What is an observable?

The ability of observables being able to handle multiple values over time makes them a good candidate for working with real-time data, events and any sort of stream you can think of.

Observables gives better control when working with in-flow of values from a stream.

Observable is a wrapper around a datasource , data source is a stream of values possibly emits multiple values over time we want to do something whenever a new value occurs.

We connect observer and observable through a subscription. Subscription says there is someone listening to these stream of values.

An observer subscribes to an Observable. An Observable emits items or sends notifications to its observers by calling the observers’ methods.

observer implements up to 3 methods.they are complete() ,next() and onerror().

next() method will be called by the observable whenever a new value is emitted.whenever observable throws an error onerror() method is invoked.when observable is done and when it knows there won’t be any more new values in the future it calls complete() method.

I strongly recommend you follow the series by academind on youtube about RXJS library to get a deeper understanding about RXJS capabilities, observers, observables, subjects, behavior subjects and subscriptions.

Let’s continue with the code

@Component({selector: 'table-basic-example',styleUrls: ['table-basic-example.css'],templateUrl: 'table-basic-example.html',})

This is regular angular 4.selector is the name by which outside parties refer to our component .and style Url is the location our /file this component refer for styling purposes and basic html of the component is found in the template url.

export class TableBasicExample {displayedColumns = ['userId', 'userName', 'progress', 'color'];exampleDatabase = new ExampleDatabase();dataSource: ExampleDataSource | null;ngOnInit() {this.dataSource = new ExampleDataSource(this.exampleDatabase);}}

Here first we make an array of all the columns of our table we need to display on the browser (column headings ) this array is referred again from the html file.

And further we create an instance of example database class and an instance of datasource which carries no data at the beginning.

Then the data in the example database is injected to this datasource to fill it as it is initially empty

Ng onitint is a life cycle hook that is called by angular in order to mark the completion of creation of the component.

const COLORS = ['maroon', 'red', 'orange', 'yellow', 'olive', 'green', 'purple','fuchsia', 'lime', 'teal', 'aqua', 'blue', 'navy', 'black', 'gray'];const NAMES = ['Maia', 'Asher', 'Olivia', 'Atticus', 'Amelia', 'Jack','Charlotte', 'Theodore', 'Isla', 'Oliver', 'Isabella', 'Jasper','Cora', 'Levi', 'Violet', 'Arthur', 'Mia', 'Thomas', 'Elizabeth'];

The we have 2 arrays .these fill up our example database

export interface UserData {id: string;name: string;progress: string;color: string;}

Here you have an interface defined. The variables mentioned here will eventually become the columns of our tables.

export class ExampleDatabase {dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);get data(): UserData[] { return this.dataChange.value; }constructor() {// Fill up the database with 100 users.for (let i = 0; i < 100; i++) { this.addUser(); }}addUser() {const copiedData = this.data.slice();copiedData.push(this.createNewUser());this.dataChange.next(copiedData);}private createNewUser() {const name =NAMES[Math.round(Math.random() * (NAMES.length - 1))] + ' ' +NAMES[Math.round(Math.random() * (NAMES.length - 1))].charAt(0) + '.';return {id: (this.data.length + 1).toString(),name: name,progress: Math.round(Math.random() * 100).toString(),color: COLORS[Math.round(Math.random() * (COLORS.length - 1))]};}}

This is the example database that the datasource uses to retrieve data for the table. You only have 2 arrays defined but in fact 4 columns to be displayed. You can fill 2 columns from the data in the arrays but the data for the other 2 columns here must be generated inside this class.

So what is a subject?

Observable can’t emit values itself but we want to be able to do it ourselves .but if we need to emit new values ourselves we have to go with a subject. Subject is an observable it inherits from observable but we can call the next() method on it manually do that we can trigger a emitting of a new value. Therefore subject is an active observable. It is an observer in addition to being an observable so you can also send values to a subject in addition to subscribing to it.

What in the world is a behavior subject?

Behavior subject is a special kind of a subject but it has an initial value unlike subjects. It needs an initial value as it must always return a value on subscription even if it hasn’t received a next()

Rx.subject() subscription won’t get anything initially

Rx.behaviorsubject(‘a’) subscription get ‘a’ initially

Don’t worry about the code written with const name…we have about 20 names in the names array but we need 100 distinct names.So here we change their initials in a random pattern.

Progress is also calculated randomly.

Data change is a variable of type behavior subject. this.dataChange.next(copiedData); ..whenever a new user gets pushed in to the array that datachange is notified to the subscribers. Datachange is a sort of a Stream that emits whenever the data has been modified.

Make a variable called datachange that is a behavior subject that has an array of initial values

Make an array called copied data

Create a new user with 4 properties. Users are created like objects here with these 4 properties being their attributes.

Call the next method with the new user added

Call the next method on the special observable and subject emits.add user() adds a user to the database while create user method builds a user object with 4 distinct attributes.

Data source to provide what data should be rendered in the table. Note that the data source can retrieve its data in any way. In this case, the data source is provided a reference to a common data base, ExampleDatabase. Datasource only take the data and send the table exactly what should be rendered, nothing else.

export class ExampleDataSource extends DataSource<any> {constructor(private _exampleDatabase: ExampleDatabase) {super();}connect(): Observable<UserData[]> {return this._exampleDatabase.dataChange;}disconnect() {}}

Connecting the table to data source

Data is provided to the table through a DataSource.

Connect function connects a collection viewer such as a data table to a datasource. Connect function is called by the table to get the stream containing the data that should be rendered. Parameters has to be given to this connect function.

When the table receives a data source, it calls the DataSource’s connect function which returns an observable that emits an array of data.

Whenever the data source emits data to this stream, the table will update.

Because the data source provides this stream, it bears the responsibility of triggering table updates. This can be based on anything: web socket connections, user interaction, model updates, time-based intervals, etc. Most commonly, updates will be triggered by user interactions like sorting and pagination.

Disconnect function breaks the connection between the table and the datasource.

Lets look at the HTML file or our template.

Cell templates

<ng-container cdkColumnDef="color">
<md-header-cell *cdkHeaderCellDef>Color</md-header-cell>
<md-cell *cdkCellDef="let row" [style.color]="row.color"> {{row.color}} </md-cell>

Firstly columns of the table are defined.with the directive cdkColumnDef each column is given a name.this is the name from which this particular column in the table is referred from other places.each column then goes on to define a header cell template and a data cell template.header cell provides and displays the name of the column and cell template retrieves data that should be displayed and displays them below the header in rows.

cdkCellDef exports row context

tables header row and data row are defined below

Row Templates are given below,

<md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
<md-row *cdkRowDef="let row; columns: displayedColumns;"></md-row>

‘Displayed columns’ array is in your .ts (typescript file) and color is a column or an element of the array (as per our example).

These row templates looks at the name given to the cdkColumnDef and finds the specific columns to render.

The cdkRowDef also exports row context. Rendered content of the row comes from the cell templates not from the row templates.

For further reading on this check this out

how to generate required Columns dynamically ?

<ng-container *ngFor="let col of displayedColumns" cdkColumnDef= {{col}}>
<md-header-cell *cdkHeaderCellDef md-sort-header > {{ col }} </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row[col]}} </md-cell>

compare this piece of code with the one before the last .here we loop through the displayedColumns array and column names are assigned in the process therefore we generate required columns looping through the array instead of defining all required columns by hand in the HTML file.

With this I will conclude this explanation, md tables further offers you features like pagination, sorting and filtering. The official documentation includes examples that you can refer to in order to add these features to your md table.

In the future i am hoping to do an extension of this , to facilitate you to understand how to md tables work with above mentioned additional features.

That’s it

Now you know what happens behind the scenes in a md table.




Software Engineering Intern @ mubasher technologies

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Internal representation of objects in JavaScript

Adding React.js to old Django site (day 4)— React finally :)

Create an Extensible REST Web Service for Google Sheets using Apps Script

Kill the man 🔫

How to deploy React Github pages

What to Know About Working with Vue.js after Using React

Creating a web server with the Node.js HTTP module

Opening system dialogs in Electron from the renderer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Sithija Thewahettige

Sithija Thewahettige

Software Engineering Intern @ mubasher technologies

More from Medium

Creating custom pie charts with D3 using Angular 13

Hurray a custom pie chart with Angular CLI 13 and D3

Angular Vs Vue: Which Framework to Choose in 2022?

Angular directive: behavior without template

Clean Code: Vue.js Edition