Tech Review: Angular Stack (1/many)

Being the first post in my Tech Review series, I wanted to start with something I use often, so I'm gonna start writing about two components that are really common in enterprise applications, a grid and a UI notification mechanism.

Also, this time I'm not doing any type of comparison between the component I'm reviewing and others, nor I'm going to give a calification, this post is going to focus on the good things of each one, and some examples on using them. 

Grid Component

Let's start with the grid component. According with npmjs, at the time of writing this post, there has been more than 50.000 downloads in the last week, it's MIT licensed and currently on version 13.0.1. Also, according to github, this repository has over 2.700 stars, 111 contributors, and a pretty active community (I hope that with all of those stats, it gives enough background about how big it is). Bottom line, I present to you, if you didn't know about it, Swimlane's grid component.

First question in my review, why do I use it? and to answer that I have two words only: features and simplicity. I like components that are simple to use for the most basic requirements that I need to fulfill; and for more complex things, I use them as base for my custom made components. 

For this grid in particular, here are some of the features available out-of-the-box (taken from their main page)[1]:

  • Handle large data sets ( Virtual DOM )
  • Expressive Header and Cell Templates
  • Horizontal & Vertical Scrolling
  • Column Reordering & Resizing
  • Client/Server side Pagination & Sorting
  • Intelligent Column Width Algorithms ( Force-fill & Flex-grow )
  • Cell & Row Selection ( Single, Multi, Keyboard, Checkbox )
  • Fixed AND Fluid height
  • Row Detail View

The features I like the most are the column reordering, pagination, sorting, custom commands and the flexible fluid layout. Those are the ones that I care the most and covers almost the principal use cases that I've seen.

Second question, how do I use it? well, normally if you use a grid in an application, I'm pretty sure you will end up using it in more places, so the first thing I do is create a custom component that wraps the grid and enables more features that do not come out-of-the-box. For example, this is the current version of the GridComponent I'm using:

import { Component, Input, Output, EventEmitter, OnInit, ViewChild, SimpleChanges } from '@angular/core';
import { DatatableComponent } from '@swimlane/ngx-datatable';
import {Md5} from 'ts-md5/dist/md5';

@Component({
  selector: 'grid',
  templateUrl: './grid.component.html',

})

export class GridComponent implements OnInit {
    @Input() filtered: boolean = false;
    @Input() data: any;
    @Input() columns: any;
    @Input() rowClass: any;
    @Input() selectionType: string = 'single';
    @Output() rowSelected: EventEmitter<any> = new EventEmitter();
    @ViewChild('table') table: DatatableComponent;

    rows = [];
    lastSelectedRowHash;
    selectedRow = [];

    constructor() { }

    onRowSelected($event) {
        if(this.selectedRow == null) return;
        if(this.selectedRow.length <= 0) return;
        var stringifyObject = JSON.stringify(this.selectedRow[0]);
        var selectedObjectHash = Md5.hashStr(stringifyObject);
        if(this.lastSelectedRowHash == selectedObjectHash)
            return;
        this.lastSelectedRowHash = selectedObjectHash;
        this.rowSelected.emit(this.selectedRow);
    }

    cancelSelection(){
        this.selectedRow = [];
        this.lastSelectedRowHash = "";
    }

    updateFilter(event) {
        if(!this.filtered) return;
        const val = event.target.value.toLowerCase();
        const temp = this.data.filter((d) => {
            var matches = false;
            for(var index = 0; index < this.columns.length; index++){
                var property = this.columns[index].prop;
                var data = d[property] == null ? "" : d[property].toString();
                matches = data.toLowerCase().indexOf(val) !== -1 || !val;
                if(matches) break;
            }
            return matches;
        });
        this.rows = temp;
        this.table.offset = 0;
    }

    ngOnInit() {
        if(this.data){
            this.rows = this.data;
        }
    }

    ngOnChanges(changes: SimpleChanges){
        for(var prop in changes){
            if(prop == 'data'){
                this.rows = changes[prop].currentValue;
            }
        }
    }
}

As you can see, I've added things such as saving the last state of the selected element to avoid firing the event unnecessarily, an easy way to unselect all elements and filtering. Obviously, this has an HTML counterpart:

<div class="row mb-3">
    <div class="col-md-4">
        <input type='text'
            class="form-control"
            placeholder='Type to filter...'
            (keyup)='updateFilter($event)'
            *ngIf="filtered"/>
    </div>
</div>
<div class='panel-body'>
    <ngx-datatable #table
        class="material selection-cell"
        [rows]="rows"
        [loadingIndicator]="loadingIndicator"
        [columns]="columns"
        [columnMode]="'force'"
        [headerHeight]="40"
        [footerHeight]="40"
        [limit]="10"
        [rowHeight]="'auto'"
        [reorderable]="reorderable"
        [selectionType]="selectionType"
        [selected]="selectedRow"
        (select)='onRowSelected($event)'
        [rowClass]="rowClass">
    </ngx-datatable>
</div>

Creating an HTML wrapper allowed me to centralize common configurations like the column mode for the fluid layout I mentioned before, or the height of the rows, among other things.

UI Notification Mechanism

Continuing with the second component of this review, let's give some stats like before. According to npmjs, at the time of writing this post, there has been more than 20.000 downloads in the last week, it's MIT licensed and currently on version 8.8.0. Also, according to github, this repository has 545 stars and 17contributors. Bottom line, I present to you, if you didn't know about it, Scott Cooper's ngx-toastr.

So, why do I use it? again, simplicity is something that I looked for, but more important this time, I needed a component that satisfied all my use cases from start and matches the UI style without interfering with the layout design. For all those reasons, ngx-toastr met all my requirements and exceeded my expectations.

Here are some of the features available (again, taken from their main page) [2]:

  • Toast Component Injection without being passed ViewContainerRef
  • No use of *ngFor. Fewer dirty checks and higher performance.
  • AoT compilation and lazy loading compatible
  • Component inheritance for custom toasts
  • SystemJS/UMD rollup bundle
  • Animations using Angular's Web Animations API
  • Output toasts to an optional target directive

Then, how do I use it? Apart from following the installation and configuration instructions, I've done nothing. This is an awesome plug-and-play component, but if you prefer, you can customize the UI design simply by following a few extra steps.

Summary

Reaching this point, I've nothing else to say except that both are excellent components that will work great in whatever project you need to work on. And hopefully, this little post makes you want to try them. 

If you have any comment, don't hesitate in contacting me or leaving a comment below. And remember to follow me on twitter to get updated on every new post.