Building a Generic Mat Table component in Angular

Building a Generic Mat Table component in Angular

You now have everything needed to create your own generic table with Angular Material. Just be creative, and you’ll build a powerful table to reuse in every project.

In this guide, I’ll show you simply how to create a generic table with Angular Material.

First and foremost, it’s important you understand the decorator concept in Typescript and how MatTable works.

Originally, the concept was thought up by my colleague and friend Francisco, and I just reworked his idea to extend it more easily with more options.

At the end, you’ll have a model to manage how to display your data in your table, like this:

import { Column } from "../decorators/column";
import { autoserializeAs } from "cerialize";

export class Car {
  @autoserializeAs(Number)
  id: number;
  @autoserializeAs(String)
  @Column()
  maker: string;
  @autoserializeAs(String)
  @Column({
    order: 1,
    canSort: true,
  })
  model: string;
  @autoserializeAs(Number)
  @Column({
    canSort: true,
  })
  year: number;
}

gmt_car.model.v4.ts

Sometimes our application contains several tables that we have to manage, and the more we have, the more maintenance becomes heavy. However, time is an important factor in a developer’s life.

Rather than creating a library to maintain, I explain step by step how you can create your own generic table and extend it with some examples. Like this, you can extend everything following your needs.

Step 0: Install the Necessary node_modules

Required:

  • @angular/material (ng add @angular/material)
  • reflect-metadata(to access metadata properties)

Optional:

  • lodash (set of handy functions)
  • cerialize (a powerful tool to map JSON properties in a JS prop class. To install it, please use [email protected]instead of cerialize)
Step 1: Prepare Your Table Component

Let’s begin by creating our car model:

export class Car {
  id: number;
  maker: string;
  model: string;
  year: number;
}

gmt_car.model.v1.ts

To mock the data, I chose Mockaroo, created the JSON file, put it in my assets folder, and then in my app.component.ts, I typed the following:

import { Component, OnInit } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { Car } from "./resources";
import { Observable } from "rxjs";
import { DeserializeArray } from "cerialize";
import { JsonArray } from 'cerialize/dist/util';

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.scss"],
})
export class AppComponent implements OnInit {
  cars$: Observable<Car[]>;

  constructor(private http: HttpClient) {}

  ngOnInit() {
    this.cars$ = this.http.get("/assets/car.json").pipe(
      map((res: JsonArray) => DeserializeArray(res, Car)),
      tap(res => console.log(res))
    );
  }
}

gmt_app.component.ts

Now, we’re ready to create our table component. Generate the component using the CLI — then your code should look like this:

<table mat-table [dataSource]="data">
  <ng-container [matColumnDef]="column" *ngFor="let column of columns">
    <th mat-header-cell *matHeaderCellDef>{{ column }}</th>
    <td mat-cell *matCellDef="let element">{{ element[column] }}</td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>

gmt_table.component.v1.html

import { Component, OnInit, Input } from "@angular/core";
import { TableModel } from "./../decorators/table.model";
import { sortBy } from "lodash";

@Component({
  selector: "app-table",
  templateUrl: "./table.component.html",
  styleUrls: ["./table.component.scss"],
})
export class TableComponent implements OnInit {
  private _data: any[];

  @Input() set data(values: any[]) { // Because AsyncPipe is used
    if (values) {
      this._data = cloneDeep(values);
    }
  }
  get data(): any[] {
    return this._data;
  }

  displayedColumns: string[];

  constructor() {}

  ngOnInit() {}
}

gmt_table.component.v1.ts

Finally, call your table component in the app.component.html:

<app-table [data]="cars$ | async"></app-table>

gmt_app.component.html

Step 2: The Decorator Column
import { autoserializeAs } from "cerialize";

export class Car {
  @autoserializeAs(Number)
  id: number;
  @autoserializeAs(String)
  @Column()
  maker: string;
  @autoserializeAs(String)
  @Column()
  model: string;
  @autoserializeAs(Number)
  @Column()
  year: number;
}

gmt_car.model.v2.ts

autoserializeAs is used by cerializeto automatically bind the properties from a JSON file to a JS object instance. You just need to provide the type. See the docs.

Column is the decorator that’ll allow us to select the properties of an object we want to display in our table. Later, we’ll see we’ll be able to add options that’ll allow us to manage the behavior of a property.

Before creating our decorator, it would be interesting to have two classes:

  • One to manage all our columns (TableModel)
  • One to manage all the options for a specific column (ColumnModel)
import { ColumnModel } from "./column.model";
export class TableModel {
  columns: ColumnModel[] = [];

  addColumn(column: ColumnModel) {
    this.columns = [...this.columns, column];
  }
}

gmt_table.model.ts

export class ColumnModel {
  /** List of options */
  key: string;

  constructor(options: Partial<ColumnModel> = {}) {
    this.key = options.key;
  }
}

gmt_column.model.v1.ts

Now, we have everything to build our decorator.

import { ColumnModel } from "./column.model";
import { TableModel } from "./table.model";

export const tableSymbol = Symbol("table");

export function Column(options: Partial<ColumnModel> = {}) {
  return function(target: any, propertyKey: string) {
    if (!target[tableSymbol]) {
      target[tableSymbol] = new TableModel();
    }
    options.key = options.key || propertyKey;
    const columnOptions = new ColumnModel(options);
    target[tableSymbol].addColumn(columnOptions);
  };
}

gmt_column.v1.ts

What happens here? First, we create a symbol we name table (it’s just for debugging — you can call it as you wish). This symbol will be in every instance of the object and allow us to access, by reference, the instance of TableModel for that object.

We check if an instance of TableModel exists — otherwise, we create it.

Then we build our ColumnModel instance with the options we get from the decorator.

Step 3: Retrieve Columns in Our Generic Table
import { Component, OnInit, Input } from "@angular/core";
import { tableSymbol } from "../decorators/column";
import { ColumnModel } from "./../decorators/column.model";
import { TableModel } from "./../decorators/table.model";
import { cloneDeep } from "lodash";

@Component({
  selector: "app-table",
  templateUrl: "./table.component.html",
  styleUrls: ["./table.component.scss"],
})
export class TableComponent implements OnInit {
  private _data: any[];
  private _tableModel: TableModel;

  @Input() set data(values: any[]) {
    if (values) {
      this._data = cloneDeep(values);
      this._tableModel = this._data[0][tableSymbol];
      this.buildColumns();
    }
  }
  get data(): any[] {
    return this._data;
  }

  columns: ColumnModel[];
  displayedColumns: string[];

  constructor() {}

  ngOnInit() {}
  
  private buildColumns() {
    this.columns = this._tableModel.columns;
    this.displayedColumns = this.columns.map(col => col.key);
  }
}

gmt_table.component.v2.ts

As we saw in our AppComponent, I created a new instance of my car object. By doing that, I triggered the decorator, so I created my TableModel that contains all the information of the columns to display in my table.

In my TableComponent, I can retrieve an instance from the list of data, and through the symbol, the instance of the TableModel. It only remains to create our columns, and then you’ll have a generic table by model.

Pay attention to adapt the HTML as well. column is now an object, not a string. column.key is the right property to use, as it represents the propertyKey in our decorator (or the custom one you provided).

Going Further

Example 1: Manage the order of our columns

For now, columns are displayed following the order of the properties defined in your model. It could be interesting, in some cases, to change this order.

export class ColumnModel {
  /** List of options */
  key: string;
  order: number;

  constructor(options: Partial<ColumnModel> = {}) {
    this.key = options.key;
    this.order = options.order || 0;
  }
}

gmt_column.model.v2.ts

import { Column } from "../decorators/column";
import { autoserializeAs } from "cerialize";

export class Car {
  @autoserializeAs(Number)
  id: number;
  @autoserializeAs(String)
  @Column()
  maker: string;
  @autoserializeAs(String)
  @Column({
    order: 1,
  })
  model: string;
  @autoserializeAs(Number)
  @Column()
  year: number;
}

gmt_car.model.v3.ts

Now, we’ve explicitly stated all columns have an order of 0 — except the model, which has an order of 1. This column should therefore be in the last position. It only remains to create a small method to sort the columns in our TableComponent.

import { Component, OnInit, Input } from "@angular/core";
import { tableSymbol } from "../decorators/column";
import { ColumnModel } from "./../decorators/column.model";
import { TableModel } from "./../decorators/table.model";
import { sortBy } from "lodash";

...

  constructor() {}

  ngOnInit() {}

  private buildColumns() {
    this.columns = this._tableModel.columns;
    this.sortColumns(); // Sort columns by order
    this.displayedColumns = this.columns.map(col => col.key);
  }

  private sortColumns() {
    this.columns = sortBy(this.columns, ["order"]);
  }
}

gmt_table.component.v3.ts

That’s it.

Example 2: Get the type of property

This example can be interesting if you want to know the type of property that you’ll use in the table. If we detect a date, it’d be interesting to be able to put it in a format. Or if it’s a number, it’d be nice to align it to the right by default — it’s up to you!

Be sure to enable these two options in your tsconfig.json:

experimentalDecorators: true

emitDecoratorMetadata: true

import { ColumnModel } from "./column.model";
import { TableModel } from "./table.model";
import "reflect-metadata";

export const tableSymbol = Symbol("column");

export function Column(options: Partial<ColumnModel> = {}) {
  return function(target: any, propertyKey: string) {
    ...
    
    // Get the type of the property
    const propType = Reflect.getMetadata("design:type", target, propertyKey);
    options.propertyType = propType.name;
    
    ...
  };
}

gmt_column.v2.ts

export class ColumnModel {
  /** List of options */
  key: string;
  order: number;
  propertyType: string;

  constructor(options: Partial<ColumnModel> = {}) {
    this.key = options.key;
    this.order = options.order || 0;
    this.propertyType = options.propertyType;
  }
}

gmt_column.model.v3.ts

Example 3: Choose columns you want to sort

By default, we have no sort enabled for our table. That’s not a problem — we just need to adapt our table.

Let’s add this new option:

export class ColumnModel {
  /** List of options */
  key: string;
  order: number;
  propertyType: string;
  canSort: boolean;

  constructor(options: Partial<ColumnModel> = {}) {
    this.key = options.key;
    this.order = options.order || 0;
    this.propertyType = options.propertyType;
    this.canSort = options.canSort || false;
  }
}

gmt_column.model.v4.ts

import { Column } from "../decorators/column";
import { autoserializeAs } from "cerialize";

export class Car {
  @autoserializeAs(Number)
  id: number;
  @autoserializeAs(String)
  @Column()
  maker: string;
  @autoserializeAs(String)
  @Column({
    order: 1,
    canSort: true,
  })
  model: string;
  @autoserializeAs(Number)
  @Column({
    canSort: true,
  })
  year: number;
}

gmt_car.model.v4.ts

Now we know which column can be sorted. Finally, we adapt our TableComponent (TS and HTML):

import { Component, OnInit, Input } from "@angular/core";
import { tableSymbol } from "../decorators/column";
import { ColumnModel } from "./../decorators/column.model";
import { TableModel } from "./../decorators/table.model";
import { sortBy, orderBy, cloneDeep } from "lodash";
import { Sort, SortDirection } from "@angular/material/sort";

@Component({
  selector: "app-table",
  templateUrl: "./table.component.html",
  styleUrls: ["./table.component.scss"],
})
export class TableComponent implements OnInit {
  private _data: any[];
  private _originalData: any[] = [];
  private _tableModel: TableModel;

  @Input() set data(values: any[]) {
    if (values) {
      this._data = cloneDeep(values);
      this._tableModel = this._data[0][tableSymbol];
      this.buildColumns();
      if (!this._originalData.length) {
        // Keep original order of data
        this._originalData = cloneDeep(this._data);
      }
    }
  }
  get data(): any[] {
    return this._data;
  }

  columns: ColumnModel[];
  displayedColumns: string[];

  constructor() {}

  ngOnInit() {}

  sortData(params: Sort) {
    const direction: SortDirection = params.direction;
    this.data = direction
      ? orderBy(this.data, [params.active], [direction])
      : this._originalData;
  }
  
  private buildColumns() {
    this.columns = this._tableModel.columns;
    this.sortColumns();
    this.displayedColumns = this.columns.map(col => col.key);
  }

  private sortColumns() {
    this.columns = sortBy(this.columns, ["order"]);
  }
}

gmt_table.component.v4.ts

<table mat-table [dataSource]="data" matSort (matSortChange)="sortData($event)">
  <ng-container [matColumnDef]="column.key" *ngFor="let column of columns">
    <ng-container *ngIf="column.canSort; else noSort">
      <th mat-header-cell *matHeaderCellDef mat-sort-header="{{ column.key }}">
        {{ column.key }}
      </th>
    </ng-container>
    <ng-template #noSort>
      <th mat-header-cell *matHeaderCellDef>{{ column.key }}</th>
    </ng-template>
    <td mat-cell *matCellDef="let element">{{ element[column.key] }}</td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>

gmt_table.component.v2.html

Check the TableComponent TS file. In the setter of data, we keep the original order of our data. It’ll be useful in our new method, sortData.

Don’t forget to adapt the HTML as well. We can create a simple ngIfElse to add the sort feature for that column.

Conclusion

You now have everything needed to create your own generic table with Angular Material. Just be creative, and you’ll build a powerful table to reuse in every project.

“I choose a lazy person to do a hard job. Because a lazy person will find an easy way to do it.” — Bill Gates

Here is the StackBlitz of the project, in case you want to play with it

Migrating from AngularJS to Angular

Migrating from AngularJS to Angular

Migrating from AngularJS to Angular a hybrid system architecture running both AngularJS and Angular

Intro

Dealing with legacy code/technologies is never fun and the path to migration isn’t always as straight forward as you want. If you are a small startup, trying to balance business requirements, scarce resources and aggressive deadlines, it becomes even more complicated.

This is the situation one of the startups I was advising was facing.

A bit of background

The startup was developing a SaaS for the last 2 years and (at the time) had around 15 clients worldwide. In these 2 years their code base grew pretty fast and lead to quite a lot of fast/reckless written code. There was nobody to be blame, this is pretty common in the startup world when business needs move way faster than you expect and you start sacrificing code qualify for quantity.

The system architecture was pretty simple. 
• a frontend application written in AngularJS (split into multiple modules that were selected at build time depending on the clients’ configuration)
• a backend application written in Python 2.7 and Django 1.9 using a Mysql database
• Celery for running async tasks

Each client would get their own isolated environment deployed on AWS:
• Apache in front of the Django application (deployed on multiple EC2 instances behind an ELB)
• AngularJS build deployed on individual S3 buckets with CloudFront in front of them

Path to migration

A few months before starting the migration, development was getting very slow, features were not coming out as fast, deadlines were missed and clients were reporting more issues with every update that we were rolling out. It was at this time that we started thinking more seriously about some kind of refactoring or major improvement.

We didn’t know exactly what we were going to “refactor/improve” so we started off by answering three questions (I recommend that anyone who is thinking about a migration/refactoring think really hard about the how to answer them):

1st question: Why is refactoring necessary now ?

This is a very important questions to answer because it helps you understand the value of the migration and also it helps to keep the team focused on the desired outcome. For example because i don’t like the way the code is written isn’t good enough reason. The reason has to have a clear value proposition that somehow directly or indirectly benefits the customers.

For us it was mainly three things:
 1. feature development was becoming painfully slow;
 2. code was unpredictable. we would work in one part of the application and break 3 other parts without realizing;
 3. single point of failure: only 1 engineer knew the FE code base completely and only he could develop new features on the codebase (this is out of a team of only 5 engineers)

So our goal was simple:

improve FE development velocity and remove the simple point of failure by empowering other engineers to develop FE features

2nd question: Who is going to do the migration ?

You can answer this question either now or after the 3rd question. Depending on the size of the company and on the available resources it can be one person, several people, an entire team, etc…

We were in a difficult situation. The only developer who could work on this couldn’t because he was busy building critical features for our customers. Luckily we had one senior backend engineer who wanted to get some FE exposure so he volunteered to take on the task. We also decided to time-box a proof of concept at 2 weeks. We did this because we didn’t know how long it would take to figure out a solution or whether the engineer could actually do this task since he hadn’t worked on FE before.

3rd question: What are we actually going to do ? The answer here usually involves some discovery time, a few tech proposals and a general overview of the options with the entire team while weighing the pros and cons of each.

For us one thing was clear from the start: we didn’t want to invest any resources into learning/on-boarding engineers on AngularJS. AngularJS had already entered Long Term Support and we didn’t want to have our engineers invest time in something that might not benefit them long term. This meant that refactoring the existing AngularJS code was not an option. So we started looking at Angular6 …

The migration

There a multiple approaches on how to have a hybrid app running different frameworks. After reviewing some options we decided that — for us — the best way to move forward was to simply have 2 separate FE applications deployed: the legacy AngularJS one and the new Angular one. This meant that any state on one app could not be transferred to the other application, which wasn’t such a big deal for us since only new modules were going to be developed using Angular and our modules didn’t share state with each other.

From the client’s perspective everything would look like one application, except for a page reload when they would move between the applications.

Pros to this approach

  • speed: get something up and running without untangling legacy code
  • safety: no risk of breaking the current live app since it would be a new code based deployed next to the old one (especially since a developer with no previous exposure to the project was working on it)
  • stop legacy development: we stop adding more code the an already unmanageable codebase

Cons to this approach:

  • maintaining legacy code: it didn’t address feature improvements on existing modules; old modules would still be in AngularJS for an undefined period of time
  • duplicating parts of the code: since the new app had to look and feel like the old one any themes, custom components would have to be written in both places. Also some parts of the layout would have to be duplicated in new app (like header, menu, etc.. ) and any changes to those components would have to be done in both apps

We already knew of a new module that we wanted to build so we started form scratch with a new Angular 6 project and we used this new module for our 2 weeks proof of concept.

Step 1— same domain

Have both apps running on the same domain so that they have access to the same cookies and local data. This was extremely important since only the AngularJS app would continue handing authentication & authorization.

Step 2— look and feel

Both apps The goal was to make the new app look the same as the original application. So we: 
 • copied over all the stylesheets
 • implemented the base layout of the application (header & menu drawer)

Step 3 — authentication & authorization

We had to duplicate the authorization logic in the Angular6 app and make sure the correct session tokens were available to allow access to the module

Step 4— routing between apps

Since our main navigation links would take you to either app, we decided do move all that logic to a backend service called menu-service. This would eliminate the need to write any navigation changes in both apps and also would allow for greater runtime control over what navigation buttons we show.

Example:

HEADER: Authorization: Bearer xxxxx
GET menu-service/v1/menu/?type=0|1 (0: legacy, 1: new)
[{
  "slug": "refresh",
  "name" : "Refresh",
  "icon" : "fa-refresh",
  "type" : 1  
 }, {
  "slug": "module1",
  "name" : "Module1",
  "icon" : "fa-module1",
  "type" : 1
}, {
  "slug": "module2",
  "name" : "Module2",
  "icon" : "fa-module2",
  "type" : 0
}, {
  "slug": "logout",
  "name" : "Logout",
  "icon" : "fa-logout",
  "type" : 0
}]

In the above example based on the type value we identify that the module1 and refresh are links towards the new application while module2 and logout are links in the old application.
This information allows each application to decide whether to use the internal routing mechanism or do a window.location redirect

Example of routing in the Angular app (AngularJS does something similar):

export class MenuService {
  constructor(private router: Router) {  }
  onMenuItemClicked(menuItem): void {
    if (menuItem.type === 1) {
      this.router.navigate([menuItem.slug])    
    } else {   
      const url = `${legacy_endpoint}/${menuItem.slug}`;
      window.location.href = url      
    } 
  }
}

Step 5— building/deployment on a real environment

Like i mentioned in the beginning the AngularJS application was deployed to an AWS S3 bucket and exposed through Cloudfront to take advantage of the massively scaled and globally distributed infrastructure offered by AWS.

The result we wanted was the following: anything that has the url [https://hostname/v2](https://hostname/v2)/ is routed to the Angular application and everything else is routed to the legacy AngularJS app.

We used base-href and to make sure our Angular6 application builds accordingly

ng build --base-href /v2/ --deploy-url /v2/

Unfortunately we didn’t manage to achieve the desired routing behavior with AWS Cloudfront. This was a big disappointment since we had to pivot to a less optimal solution. (if anyone has any suggestion on how to do this in Cloudfront i’d love to hear it)

We ended up with the following structure:
• each app deployed in a NGINX Docker container

# AngularJS — Dockerfile:
FROM nginx:alpine
COPY dist /usr/share/nginx/html
--------------------------------------------------------------------
# Angular6 — Dockerfile:
FROM nginx:alpine
COPY dist /usr/share/nginx/html/v2

• AWS ALB with path routing

Step 6: Local development

Local development for the AngularJS application didn’t have to change. However in order to develop on the Angular6 app you had to also run the AngularJS application to be able to authenticate and get the appropriate session tokens.

We were already using Docker for deploying our application as containers. So we added a Makefile target to run the latest from our Docker repository

# Angular6 — Makefile:
AWS_REPOSITORY = xxx.dkr.ecr.eu-central-1.amazonaws.com
JS_APP_NAME = angular-js
...
run-local: 
  docker run -p 8080:80 $(AWS_REPOSITORY)/$(JS_APP_NAME):latest

Conclusion

This might not be the cleanest or optimal solution, however it was the fastest way towards our goals. And this was the most important thing to us.

The goal of this post isn’t to teach you how to do a AngularJS to Angular6 migration but instead is to showcase our path when dealing with such a task.

Further reading:

An in-depth look at Angular’s ng template

Angular 8 Data (Event & Property) Binding Tutorial: Build your First Angular App

Angular 8 Forms Tutorial - Reactive Forms Validation Example

What is the difference between JavaScript and AngularJS?

JavaScript is a client-side programming language used for creating dynamic websites and apps to run in the client's browser whereas AngularJS is a fully featured web app framework established on JavaScript and managed by Google.

JavaScript is a client-side programming language used for creating dynamic websites and apps to run in the client's browser whereas AngularJS is a fully featured web app framework established on JavaScript and managed by Google.


What’s the difference between AngularJS and Angular?

What’s the difference between AngularJS and Angular?

Angular vs Angularjs - key differences, performance, and popularity

AngularJS was released in 2009 and quickly became popular for it's two-way data binding, MVC architecture, and code reusability.

When alternatives like React and Vue delivered the same advantages of AngularJS with better performance, the Angular team decided to completely rewrite the framework.

Each subsequent release of Angular (4,5,6,7,8) has been mostly non-breaking incremental changes. For these reasons, "Angular" now refers to Angular 2+ and "AngularJS" the original.

Key Differences

Here are the key differences between Angular 2+ and AngularJS:

TypeScript

Angular was rewritten using TypeScript. TypeScript is a superset of JavaScript. It compiles to regular vanilla JavaScript but provides syntax for type checking, annotations, and ES6 based extensions.

Since TypeScript is a superset of JavaScript, it needs to be compiled or "transpiled" into ES5 JavaScript so your code still runs in the browser. This requires the use of NodeJS and other build tools for preprocessing TypeScript files.

While using Angular 2+ without TypeScript is possible, the industry standard is to adopt TypeScript as it plays much better with the Angular ecosystem.

MVC vs Component Architecture

AngularJS adheres to the model, view, controller (MVC) software design pattern. Controllers are defined with $scope variables representing an underlying data model. This data model can be updated in both the view and the controller. The view is an HTML file which both displays and dynamically updates $scope variables.

Angular 2+ utilizes more of a component based architecture. Isolated pieces of functionality are defined in components. These components reference their own templates and stylesheets and exist in a hierarchy of other components.

Dependency Injection (DI)

Both AngularJS and Angular use dependency injection. DI allows you to share commonly used functionality across different controllers or components.

In AngularJS, dependencies are injected in controller functions, link functions, and directive definitions.

In Angular, constructor functions, providers, and declarations are used to manage these dependencies.

Angular CLI

Angular 2+ features the Angular CLI: a command line interface for quickly generating Angular components, services, directives, etc. It comes with convenient commands for building your Angular project (compiling TypeScript files and other assets into vanilla js files that run in the browser). It also makes building your project for different environments easier and allows for things like linting, type checking, etc.

AngularJS doesn't have it's own CLI.

Performance

Angular is much faster than AngularJS. In fact, it's said that Angular can be more than 5X faster based on the design of your application.

Popularity

Before the advent of React and Vue, AngularJS was very popular. It offered an elegant solution to the JavaScript SPA with two-way data binding and MVC architecture.

Being able to dynamically update a JavaScript POJO from an HTML template caused a lot of buzz. As a result, alternatives like React and Vue emerged with superior diffing algorithms that left AngularJS in the dust.

Angular fought back with the release of Angular 2 (2016). Today, Angular remains one of the most popular frameworks for UI development.

While AngularJS is still used today, it's popularity has died in favor of more current options like Angular 2+, React, and Vue.

Performance

The problem with AngularJS

Performance is one of the biggest problems with the original AngularJS. This is due to the underlying "magic" behind what originally made AngularJS so popular.

To achieve two-way data binding, AngularJS relies on a digest cycle to keep views in sync with their underlying data models. It works by augmenting all event handlers (clicks, ajax, timeouts) with a process called "dirty checking". Each scoped variable is compared to it's previous value.

If something has changed, the watchers and templates are updated with the new value and the process runs again to see if anything else has changed. In this way, the view is constantly in sync with the data model.

The problem with the AngularJS digest cycle is it's unpredictable. As applications grow, the "checking" process becomes more intensive and can run infinitely with two way data flow.

Angular 2+ to the rescue

To address these issues, the Angular team rewrote the framework with flux architecture in mind. Specifically unidirectional data flow was fundamental to reengineering change detection in Angular.

Now the Angular framework is just as fast as alternatives. When compared to AngularJS, Angular can be more than 5X faster.

The Angular CLI also makes minifying production bundle sizes a breeze, keeping Angular light weight for production.

Advantages of Angular

Angular offers many advantages over the original AngularJS:

Performance:

Angular is up to 5X faster than AngularJS. This is because of a superior diffing algorithm featuring unidirectional data flow and component based architecture.

Server side rendering

Angular offers extensions for rendering your application server side. This is huge for SEO as certain web crawlers can't always scrape async content.

Mobile development

As a framework, Angular makes it possible to develop applications that work on both browsers and native devices like iOS and Android.

Lazy loading

Lazy loading allows you to asynchronously load JavaScript components based on route. This can offer additional performance advantages as code is only imported when it's being used.

Tooling

The tooling provided by TypeScript and the NodeJS ecosystem can't be underestimated. Using the Angular CLI, you can quickly generate Angular components, services, directives etc. without having to manually copy / paste a bunch of boilerplate code.

Additionally, you can more easily build and deploy your project using the CLI.

Should I use Angular or AngularJS?

With the performance advantages, Angular may seem like the best bet moving forward. There is a substantial learning curve to understanding the NodeJS / TypeScript ecosystem and one of the few advantages of AngularJS is that it just runs in the browser.

Using AngularJS makes sense if you have a small application and don't want to bother with learning the ins and outs of NodeJS and TypeScript.

There are also many existing projects out there that already use AngularJS and migrating to newer versions may not justify the cost of learning and rewriting code.

Outside of these cases, adopting "Angular" over the original "AngularJS" is preferred moving forward.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading about Angular

Angular 8 (formerly Angular 2) - The Complete Guide

Angular & NodeJS - The MEAN Stack Guide

The Complete Node.js Developer Course (3rd Edition)

The Web Developer Bootcamp

Best 50 Angular Interview Questions for Frontend Developers in 2019

How to build a CRUD Web App with Angular 8.0

React vs Angular: An In-depth Comparison

React vs Angular vs Vue.js by Example

Microfrontends — Connecting JavaScript frameworks together (React, Angular, Vue etc)

Building CRUD Mobile App using Ionic 4, Angular 8

How to Build Mobile Apps with Angular, Ionic 4, and Spring Boot

Ionic 4 & Angular Tutorial For Beginners - Crash Course