Posts Tagged: javascript

Sort Table Columns with Angular and Typescript

Here is a simple method for adding sorting to any table in Angular 2+. It’s a simple approach that allows you to define how the table is filled with data, instead of the table sort forcing you to use either client side or server side.

tldr; Here is the Plunker https://plnkr.co/DITVzCSqHHB1uNrTxFit/

Let’s start with our basic table and some data. Assuming we have a component that serves this html and exposes a public property that is an array of Customers, we can do the standard *ngFor syntax to display our table data.

<table>
  <thead>
    <tr>
      <th>Id</th>
      <th>First Name</th>
      <th>Last Name</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let c of customers">
      <td>{{c.id}}</td>
      <td>{{c.firstname}}</td>
      <td>{{c.lastname}}</td>
    </tr>
  </tbody>
</table>
export class AppComponent implements OnInit { 

  customers: Customer[];

  constructor(private service: CustomerService){}

  getCustomers(){
     this.customers = this.service.getCustomers();
  }

  ngOnInit(){
    this.getCustomers();
  }

}

The service above could be a static data service like above, or you could be returning an observable and subscribing to it. The key takeaway here is you don’t need to do anything out of the ordinary to make this table sort work. Just standard Angular methods of loading data into a table.

Sort Icons

The first thing I want to be able to do is display those fancy caret icons next to the column names so I know which column is being sorted. The trick to watch out for, especially with tables, is that browsers expect <table> content to be very specific, so you can’t have angular wrapping the dom with extra elements. To that end, we need to make a component that targets a directive, instead of its own html element.

I like to start at the usage and work backwards, so expanding our html table I want the syntax to end up like this

<table>
  <thead>
    <tr>
      <th sortable-column="id">Id</th>
      <th sortable-column="firstname">First Name</th>
      <th sortable-column="lastname">Last Name</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let c of customers">
      <td>{{c.id}}</td>
      <td>{{c.firstname}}</td>
      <td>{{c.lastname}}</td>
    </tr>
  </tbody>
</table>

The sortable-column attribute adds up and down chevrons and binds a click handler to the column header. It takes a parameter that is equal to the data column name we want to sort by.

import { Component, OnInit, Input, EventEmitter, OnDestroy, HostListener } from '@angular/core';

@Component({
    selector: '[sortable-column]',
    templateUrl: './sortable-column.component.html'
})
export class SortableColumnComponent implements OnInit {

    constructor() { }

    @Input('sortable-column')
    columnName: string;

    @Input('sort-direction')
    sortDirection: string = '';

    @HostListener('click')
    sort() {
        this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
    }

    ngOnInit() { }
}
<i class="fa fa-chevron-up" *ngIf="sortDirection === 'asc'" ></i>
<i class="fa fa-chevron-down" *ngIf="sortDirection === 'desc'"></i>
<ng-content></ng-content>

Now if we click the headers, the caret changes to the indicate the sort direction.

Component Communication

We only want one column to be sorted at any given time so we need a way for the columns to communicate. We can create a service to broker between the columns when changes occur.

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class SortService {

    constructor() { }

    private columnSortedSource = new Subject<ColumnSortedEvent>();

    columnSorted$ = this.columnSortedSource.asObservable();

    columnSorted(event: ColumnSortedEvent) {
        this.columnSortedSource.next(event);
    }

}

export interface ColumnSortedEvent {
    sortColumn: string;
    sortDirection: string;
}

Now we can expand our SortableColumnComponent to subscribe to changes from other columns.

import { Component, OnInit, Input, Output, EventEmitter, OnDestroy, HostListener } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';

import { SortService } from './sort.service';

@Component({
    selector: '[sortable-column]',
    templateUrl: './sortable-column.component.html'
})
export class SortableColumnComponent implements OnInit, OnDestroy {

    constructor(private sortService: SortService) { }

    @Input('sortable-column')
    columnName: string;

    @Input('sort-direction')
    sortDirection: string = '';

    private columnSortedSubscription: Subscription;

    @HostListener('click')
    sort() {
        this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
        this.sortService.columnSorted({ sortColumn: this.columnName, sortDirection: this.sortDirection });
    }

    ngOnInit() {
        // subscribe to sort changes so we can react when other columns are sorted
        this.columnSortedSubscription = this.sortService.columnSorted$.subscribe(event => {
            // reset this column's sort direction to hide the sort icons
            if (this.columnName != event.sortColumn) {
                this.sortDirection = '';
            }
        });
    }

    ngOnDestroy() {
        this.columnSortedSubscription.unsubscribe();
    }
}

When a column is clicked, it calls the sortService.columnSorted method with the details of what changed. The SortService emits an event with that information to all subscribers. Each column listens for that event and if the sort column has changed, it hides its icons to indicate to the user the table is no longer sorted by that column. The column that emitted the event ignores the change.

Sorting the Table

To finish this up, we need a way to listen to the sort event and bubble the change to our table. We could subscribe directly to the SortService itself and that would work OK, but it gets messy if you have more than one table. Its better to encapsulate this into its own directive and bubble the event through the table since that is what we are sorting.

Again, I like to design how I want to use the directive first.

<table sortable-table (sorted)="onSorted($event)">
  <thead>
    <tr>
    <th sortable-column="id" sort-direction="asc">Id</th>
    <th sortable-column="firstname">First Name</th>
    <th sortable-column="lastname">Last Name</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let c of customers">
      <td>{{c.id}}</td>
      <td>{{c.firstname}}</td>
      <td>{{c.lastname}}</td>
    </tr>
  </tbody>
</table>

Here we can see a new attribute sortable-table and I’m using the Angular () syntax to subscribe to changes in sorting.

import { Directive, OnInit, EventEmitter, Output, OnDestroy, Input } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';

import { SortService } from './sort.service';

@Directive({
  selector: '[sortable-table]'
})
export class SortableTableDirective implements OnInit, OnDestroy {

  constructor(private sortService: SortService) {}

    @Output()
    sorted = new EventEmitter();

    private columnSortedSubscription: Subscription;

    ngOnInit() {
        this.columnSortedSubscription = this.sortService.columnSorted$.subscribe(event => {
            this.sorted.emit(event);
        });
    }

    ngOnDestroy() {
        this.columnSortedSubscription.unsubscribe();
    }
}

This directive has no visual component, but instead gives us an extension point to subscribe to changes. This allows us the flexibility of multiple tables on screen and can be more easily extended with additional features.

A full working sample can be found on Plunker. https://plnkr.co/DITVzCSqHHB1uNrTxFit/

REST API Testing with Cucumber and Javascript

There are a great many tools available for testing REST Api’s by various vendors, open source plugins, browser plugins, and the old classic curl.

If you’re looking for a really simple way to test REST Api’s that is easy to maintain and entirely automated then you should consider using Cucumber with Javascript. If you’re not familiar with Cucumber, its an adapter that handles gherkin, a domain specific language for writing ATDD tests.

Here is what a simple automated test looks like using gherkin DSL:

Scenario: Making a simple GET request
    When I make a GET request to "/posts/1"
    Then The response property "userId" should be "1"
    And The response property "id" should be "1"

Any member of your team, whether it’s a developer, tester, or analyst can read and understand the scripts, which really makes it easier to collaborate as a team and eliminate confusion.

A simple POST request might look like this

Scenario: Making a POST request using tabular data
    Given The request data
    | title | body | userId |
    | foo   | bar  | 1      |
    When I make a POST request to "/posts"
    Then The response property "userId" should be "1"
    And The response property "id" should be "101"

Or if you prefer to use json (I certainly do), you can use this alternate format

Scenario: Making a POST request with json data
    Given The json request data
    """json
    {
        "title": "foo",
        "body": "bar",
        "userId": 1
    }
    """
    When I make a POST request to "/posts"
    Then The response property "userId" should be "1"
    And The response property "id" should be "101"

I have written a small framework as a starting point that you can clone from Github and start writing tests today. The framework can be customized as you go along and expanded to cover any testing need you have.

https://github.com/carbonrobot/relish

Using Opentoken in Hapi.js

hapi-auth-opentoken

npm version

Opentoken authentication scheme designed to work with PINGFederate Opentoken endpoints.

Usage

const opentoken = {
    register: require('hapi-auth-opentoken'),
    options: {
        password: 'testPassword',
        cipherSuite: 0,
        tokenName: 'opentoken'
    }
};

server.register([opentoken], (err) => {

    server.auth.strategy('default', 'opentoken', { validateFunc: validate });
    server.route({ method: 'GET', path: '/', config: { auth: 'default' } });

});

function validate(request, token, callback) {
    // token contains the decrypted saml response

    callback(err, { id: '12345', name: 'Jos Sykes' });
}

Options

Opentoken takes the following options

  • password – the token decryption shared key (default: null)
  • cipherSuite – the opentoken cipher algorithm used by the server. One of the following [0,1,2,3] (default: 0)
  • tokenName – (required) the name of the parameter in the POST request body to parse the token from
  • tokenTolerance – The amount of time (in seconds) to allow for clock skew between servers in seconds (default: 120)
  • tokenLifetime – The duration (in seconds) for which the token is valid. (default: 300)
  • tokenRenewal – The amount of time (in seconds) the token will renew itself for. (default: 12hrs)

Validation

The validation function has a signature of validate(request, token, callback) where

  • request – the hapi.js request object
  • token – the decrypted opentoken saml information
  • callback – a callback function taking the following parameters
    • err – An optional error message which gets logged to stdout, null if no error
    • user – the user information to attach to request.auth.credentials in downstream methods