<!DOCTYPE html>
<html>

  <head>
    <title>Fuel Travel Angular2 Sortable Table</title>
    <link rel="stylesheet" href="style.css" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" />
    <link data-require="font-awesome@4.6.1" data-semver="4.6.1" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.0/es6-shim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.23/system-polyfills.js"></script>
    <script src="https://cdn.polyfill.io/v2/polyfill.js?features=Intl.~locale.en"></script>
    <script src="https://code.angularjs.org/2.0.0-beta.17/angular2-polyfills.js"></script>
    <script src="https://code.angularjs.org/tools/system.js"></script>
    <script src="https://code.angularjs.org/tools/typescript.js"></script>
    <script src="config.js"></script>
    <script src="https://code.angularjs.org/2.0.0-beta.17/Rx.js"></script>
    <script src="https://code.angularjs.org/2.0.0-beta.17/angular2.js"></script>
    <script src="https://code.angularjs.org/2.0.0-beta.17/http.js"></script>
    <script>
      System.import('app')
        .catch(console.error.bind(console));
    </script>
  </head>

  <body>
    <main class="container">
      <my-app>
        loading...
      </my-app>
    </main>
  </body>

</html>
.table-sortable > thead > tr > th {
    cursor: pointer;
    position: relative;
    background-image: none;
}
 
.table-sortable > thead > tr > th:after,
.table-sortable > thead > tr > th.sort-false:after,
.table-sortable > thead > tr > th.sort-true:after {
    font-family: FontAwesome;
    padding-left: 5px;
}

.table-sortable > thead > tr > th:after {
    content: "\f0dc";
    color: #ddd;
}
.table-sortable > thead > tr > th.sort-false:after {
    content: "\f0de";
    color: #767676;
}
.table-sortable > thead > tr > th.sort-true:after {
    content: "\f0dd";
    color: #767676;
}
### Angular2 Starter Plunker - Typescript - Beta 0

A simple plunker demonstrating Angular2 usage:
- Uses SystemJS + TypeScript to compile on the fly
- Includes binding, directives, http, pipes, and DI usage.
System.config({
  //use typescript for compilation
  transpiler: 'typescript',
  //typescript compiler options
  typescriptOptions: {
    emitDecoratorMetadata: true
  },
  //map tells the System loader where to look for things
  map: {
    app: "./src"
  },
  //packages defines our app package
  packages: {
    app: {
      main: './main.ts',
      defaultExtension: 'ts'
    }
  }
});
//main entry point
import {bootstrap} from 'angular2/platform/browser';
import {App} from './app';

bootstrap(App, [])
  .catch(err => console.error(err));
//our root app component
import {Component} from 'angular2/core'
import {CORE_DIRECTIVES} from 'angular2/common'
import {TableSortable} from './tableSortable'

@Component({
  selector: 'my-app',
  templateUrl: 'src/app.html',
  directives: [CORE_DIRECTIVES, TableSortable]
})
export class App {
  
  rows: any[] = [
    {
      Name: 'Data 1',
      Amount: 100.23,
      Date: 1433588216000
    },
    {
      Name: 'Data 2',
      Amount: 0.875623,
      Date: 1432387616000
    },
    {
      Name: 'Data 3',
      Amount: .010123,
      Date: 1461820116000
    },
    {
      Name: 'Data 4',
      Amount: 1873.02301,
      Date: 1423128616000
    },
    {
      Name: 'Data 5',
      Amount: -93,
      Date: 1439220116000
    }
  ];
  columns: any[] = [
    {
      display: 'Column 1', //The text to display
      variable: 'Name', //The name of the key that's apart of the data array
      filter: 'text' //The type data type of the column (number, text, date, etc.)
    },
    {
      display: 'Column 2', //The text to display
      variable: 'Amount', //The name of the key that's apart of the data array
      filter: 'decimal : 1.0-2' //The type data type of the column (number, text, date, etc.)
    },
    {
      display: 'Column 3', //The text to display
      variable: 'Date', //The name of the key that's apart of the data array
      filter: 'dateTime' //The type data type of the column (number, text, date, etc.)
    }
  ];
  sorting: any = {
    column: 'Name', //to match the variable of one of the columns
    descending: false
  };
}
/*
 * Example use
 *		Basic Array of single type: *ngFor="let todo of todoService.todos | orderBy : '-'"
 *		Multidimensional Array Sort on single column: *ngFor="let todo of todoService.todos | orderBy : ['-status']"
 *		Multidimensional Array Sort on multiple columns: *ngFor="let todo of todoService.todos | orderBy : ['status', '-title']"
 */

import {Pipe, PipeTransform} from 'angular2/core';

@Pipe({name: 'orderBy', pure: false})
export class OrderBy implements PipeTransform {

	value:string[] =[];

	static _orderByComparator(a:any, b:any):number{
        
        if(a === null || typeof a === 'undefined') a = 0;
        if(b === null || typeof b === 'undefined') b = 0;

		if((isNaN(parseFloat(a)) || !isFinite(a)) || (isNaN(parseFloat(b)) || !isFinite(b))){
			//Isn't a number so lowercase the string to properly compare
			if(a.toLowerCase() < b.toLowerCase()) return -1;
			if(a.toLowerCase() > b.toLowerCase()) return 1;
		}
		else{
			//Parse strings as numbers to compare properly
			if(parseFloat(a) < parseFloat(b)) return -1;
			if(parseFloat(a) > parseFloat(b)) return 1;
		}

		return 0; //equal each other
	}

    transform(input:any, config:string = '+'): any{

    	//make a copy of the input's reference
    	this.value = [...input];
    	var value = this.value;
        
        if(!Array.isArray(value)) return value;

        if(!Array.isArray(config) || (Array.isArray(config) && config.length == 1)){
            var propertyToCheck:string = !Array.isArray(config) ? config : config[0];
            var desc = propertyToCheck.substr(0, 1) == '-';
            
            //Basic array
            if(!propertyToCheck || propertyToCheck == '-' || propertyToCheck == '+'){
                return !desc ? value.sort() : value.sort().reverse();
            }
            else {
                var property:string = propertyToCheck.substr(0, 1) == '+' || propertyToCheck.substr(0, 1) == '-'
                    ? propertyToCheck.substr(1)
                    : propertyToCheck;

                return value.sort(function(a:any,b:any){
                    return !desc 
                        ? OrderBy._orderByComparator(a[property], b[property]) 
                        : -OrderBy._orderByComparator(a[property], b[property]);
                });
            }
        }
        else {
            //Loop over property of the array in order and sort
            return value.sort(function(a:any,b:any){
                for(var i:number = 0; i < config.length; i++){
                    var desc = config[i].substr(0, 1) == '-';
                    var property = config[i].substr(0, 1) == '+' || config[i].substr(0, 1) == '-'
                        ? config[i].substr(1)
                        : config[i];

                    var comparison = !desc 
                        ? OrderBy._orderByComparator(a[property], b[property]) 
                        : -OrderBy._orderByComparator(a[property], b[property]);
                    
                    //Don't return 0 yet in case of needing to sort by next property
                    if(comparison != 0) return comparison;
                }

                return 0; //equal each other
            });
        }
    }
}
<table-sortable
  [columns]="columns"
	[data]="rows"
	[sort]="sorting">
  Loading table...
</table-sortable>
import {Component, Input} from 'angular2/core'
import {OrderBy} from "./orderBy"
import {Format} from "./format"

@Component({
  selector: 'table-sortable',
  templateUrl: 'src/tableSortable.html',
  pipes: [OrderBy, Format]
})
export class TableSortable {
  
  @Input() columns: any[];
  @Input() data: any[];
  @Input() sort: any;
  
  selectedClass(columnName): string{
    return columnName == this.sort.column ? 'sort-' + this.sort.descending : false;
  }
  
  changeSorting(columnName): void{
    var sort = this.sort;
    if (sort.column == columnName) {
      sort.descending = !sort.descending;
    } else {
      sort.column = columnName;
      sort.descending = false;
    }
  }
  
  convertSorting(): string{
    return this.sort.descending ? '-' + this.sort.column : this.sort.column;
  }
}
<table class="table table-hover table-striped table-sortable">
  <thead>
    <tr>
      <th *ngFor="let column of columns" [class]="selectedClass(column.variable)" (click)="changeSorting(column.variable)">
        {{column.display}}
      </th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let object of data | orderBy : convertSorting()">
      <td *ngFor="let column of columns">
        {{object[column.variable] | format : column.filter}}
      </td>
    </tr>
  </tbody>
</table>
import {Pipe, PipeTransform} from 'angular2/core';
import {DatePipe, DecimalPipe} from 'angular2/common';

@Pipe({
  name: 'format'
})
export class Format implements PipeTransform  {
  
  datePipe: DatePipe = new DatePipe();
  decimalPipe: DecimalPipe = new DecimalPipe();
  
  transform(input:string, args:any): any {
    var format = '';
    var parsedFloat = 0;
    var pipeArgs = args.split(':');
    for(var i = 0; i < pipeArgs.length; i++){
      pipeArgs[i] = pipeArgs[i].trim(' ');
    }
    
    switch(pipeArgs[0].toLowerCase()) {
      case 'text':
        return input;
      case 'decimal':
      case 'number':
        parsedFloat = !isNaN(parseFloat(input)) ? parseFloat(input) : 0;
        format = pipeArgs.length > 1 ? pipeArgs[1] : null;
        return this.decimalPipe.transform(parsedFloat, format);
      case 'percentage':
        parsedFloat = !isNaN(parseFloat(input)) ? parseFloat(input) : 0;
        format = pipeArgs.length > 1 ? pipeArgs[1] : null;
        return this.decimalPipe.transform(parsedFloat, format) + '%';
      case 'date':
      case 'datetime':
        var date = !isNaN(parseInt(input)) ? parseInt(input) : new Date(input);
        format = 'MMM d, y h:mm:ss a';
        if(pipeArgs.length > 1)
        {
            format = '';
            for(var i = 1; i < pipeArgs.length; i++){
                format += pipeArgs[i];
            }
        }
        return this.datePipe.transform(date, format);
      default:
        return input;
    }
  }
}