<!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;
}
}
}