Skip to content
This repository has been archived by the owner on Feb 2, 2019. It is now read-only.

Commit

Permalink
feat(data-table): Add Column Sorting
Browse files Browse the repository at this point in the history
Add column sorting via an attribute direcive which styles its host,
subsribes to and calls method on a service with an observable model.

Closes #144
  • Loading branch information
samuel.jones committed Jul 28, 2016
1 parent dbfc4e8 commit 84a5f82
Show file tree
Hide file tree
Showing 12 changed files with 573 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<md-data-table layout-fill>
<thead>
<tr>
<th class="md-text-cell" md-data-column-sort="name">Picnic Item</th>
<th md-data-column-sort="quantity">Quantity</th>
<th md-data-column-sort="price">Price</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of picnicItems">
<td class="md-text-cell">{{ item.name }}</td>
<td>{{ item.quantity }}</td>
<td>&pound;{{ item.price }}</td>
</tr>
</tbody>
</md-data-table>
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {
Component,
OnDestroy
} from '@angular/core';
import {
MdDataColumnSortDirective, MdDataColumnSortingService, SortDirection
} from 'ng2-material';
import {Subscription} from 'rxjs/Subscription';


@Component({
moduleId: module.id,
selector: 'data-table-sorting',
providers: [MdDataColumnSortingService],
directives: [MdDataColumnSortDirective],
templateUrl: 'data-table-sorting.component.html',
})
export class DataTableSortingComponent implements OnDestroy {

private colSortChanges: Subscription;

picnicItems: Array<any> = [
{'name': 'Wine', 'quantity': 12, 'price': 22.90},
{'name': 'Cheese', 'quantity': 4, 'price': 32.25},
{'name': 'Crackers', 'quantity': 117, 'price': 2.35}
]

constructor(private sortingService: MdDataColumnSortingService) {
// assign default
this.sortingService.setSorting({ column: 'name', direction: SortDirection.ASCEND });

// respond to changes
this.colSortChanges = this.sortingService.sortingColumn.subscribe((col) => {
this.picnicItems.sort(function (a, b) {
let valA = col.direction == SortDirection.ASCEND ? a[col.column] : b[col.column];
let valB = col.direction == SortDirection.DESCEND ? a[col.column] : b[col.column];

return valA < valB ? -1 : valA > valB ? 1 : 0;
});
});
}

ngOnDestroy() {
this.colSortChanges.unsubscribe();
}
}
3 changes: 2 additions & 1 deletion modules/site/src/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {DataTableBasicUsageComponent} from './examples/data-table/data-table-bas
import {
DataTableSelectableRowsComponent
} from './examples/data-table/data-table-selectable-rows.component';
import {DataTableSortingComponent} from './examples/data-table/data-table-sorting.component';
import {DialogBasicUsageComponent} from './examples/dialog/dialog-basic-usage.component';
import {ElevationBasicUsageComponent} from './examples/elevation/elevation-basic-usage.component';
import {InputBasicUsageComponent} from './examples/input/input-basic-usage.component';
Expand All @@ -33,7 +34,7 @@ export {SiteAppComponent} from './site.component';
export const DEMO_DIRECTIVES: any[] = [
ElevationBasicUsageComponent, ButtonBasicUsageComponent, CardActionButtonsComponent,
CardBasicUsageComponent, CardInlineActionsComponent, CheckboxBasicUsageComponent,
DataTableBasicUsageComponent, DataTableSelectableRowsComponent, DialogBasicUsageComponent,
DataTableBasicUsageComponent, DataTableSelectableRowsComponent, DataTableSortingComponent, DialogBasicUsageComponent,
InputBasicUsageComponent, ListBasicUsageComponent, PaginationBasicUsageComponent, PaginationSplitUsageComponent,
ProgressBarBasicUsageComponent, ProgressCircleBasicUsageComponent, RadioBasicUsageComponent, SidenavBasicUsageComponent,
SwitchBasicUsageComponent, TabsDynamicHeightComponent, TabsDynamicTabsComponent,
Expand Down
82 changes: 82 additions & 0 deletions src/components/data-table/column-sorting/column-sort.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {
Directive,
Input,
HostBinding,
HostListener,
OnDestroy
} from "@angular/core";
import {Subscription} from 'rxjs/Subscription';
import {
MdDataColumnSortingService,
SortDirection,
ColumnSortingModel
} from './column-sort.service';

/**
* @name mdDataSortColumnDirective
*
* @description
* A directive that styles its host with sorting class names, and calls
* {@link MdDataColumnSortingService.changeSorting} to the service instance in its
* execution context.
*
* @usage
*
* <hljs lang="html">
* <tr>
* <th md-data-column-sort="1">Name</th>
* <th md-data-column-sort="deux">Occupation</th>
* <th md-data-column-sort="your_id_format_3">Status</th>
* <tr>
* </hljs>
*/
@Directive({
selector: '[md-data-column-sort]'
})
export class MdDataColumnSortDirective implements OnDestroy {

/**
* Sorting value of the group of columns.
* The group is defined by the injection scope of the {@link MdDataColumnSortingService} in the parent component.
*/
sortingColumn: ColumnSortingModel;
sortingSubscription: Subscription;

/** Unique column identifier */
@Input('md-data-column-sort') mdDataSortColumn: string;

/** Applies 'sortable' CSS class to host */
@HostBinding('class.sortable') get sortable() { return true; }

/** Conditionally applies 'sorted-ascending' CSS class to host */
@HostBinding('class.sorted-ascending') get sortingAscending() {
return this.isCurrentColumn() && this.sortingColumn.direction === SortDirection.ASCEND;
}

/** Conditionally applies 'sorted-descending' CSS class to host */
@HostBinding('class.sorted-descending') get sortingDescending() {
return this.isCurrentColumn() && this.sortingColumn.direction === SortDirection.DESCEND;
}

/**
* Click handler on host element to tell service to sort by this column
* @param clickEvent - the clickEvent.
*/
@HostListener('click', ['$event']) sortBy(clickEvent: Event) {
this.sortingService.onColumnSelect(this.mdDataSortColumn, this.sortingColumn);
}

constructor(private sortingService: MdDataColumnSortingService) {
this.sortingSubscription = this.sortingService.sortingColumn.subscribe(
(sorting: ColumnSortingModel) => this.sortingColumn = sorting);
}

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

private isCurrentColumn():boolean {
return this.sortingColumn && this.sortingColumn.column === this.mdDataSortColumn
}

}
52 changes: 52 additions & 0 deletions src/components/data-table/column-sorting/column-sort.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {Injectable} from '@angular/core';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';

/** Specifies sorting direction */
export enum SortDirection {
ASCEND, DESCEND
}

/** Sorting model. */
export interface ColumnSortingModel {
column: string;
direction: SortDirection;
}


/**
* Service handling column sort change events as an observable stream.
*/
@Injectable()
export class MdDataColumnSortingService {
public sortingColumn: BehaviorSubject<ColumnSortingModel> = new BehaviorSubject(
{ column: 'NONE', direction: SortDirection.ASCEND });

/**
* Programatically set the column / direction of sorting
* @param sorting column & direction to sort by.
*/
public setSorting(sorting: ColumnSortingModel) {
this.sortingColumn.next(sorting);
}

/**
* Respond to user action on a particular column heading.
* Inputting the currently sorted-by column will invert the sort direction.
* @param id identifier of column to sort by
* @param sorting current sorting model to compare against id
*/
public onColumnSelect(fromId:string, sorting:ColumnSortingModel) {
let newSort: ColumnSortingModel = {
column: fromId,
direction: SortDirection.ASCEND
};

if (sorting && sorting.column === fromId) {
// invert currently selected column
newSort.direction = sorting.direction === SortDirection.DESCEND ?
SortDirection.ASCEND : SortDirection.DESCEND;
}

this.setSorting(newSort);
}
}
Loading

0 comments on commit 84a5f82

Please sign in to comment.