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

Commit

Permalink
feat: revamp interaction between table and rows. Now it only works wi…
Browse files Browse the repository at this point in the history
…th EventEmitter
  • Loading branch information
Gregcop1 committed Mar 31, 2016
1 parent aee2a3b commit 115ba65
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 175 deletions.
4 changes: 2 additions & 2 deletions examples/components/data_table/selectable_usage.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
</md-toolbar>
<md-data-table layout-fill [selectable]="true" (onSelectableChange)="change($event)">
<thead>
<tr md-data-table-header-row>
<tr md-data-table-header-selectable-row>
<th class="md-text-cell">Material</th>
<th>Quantity</th>
<th>Unit price</th>
</tr>
</thead>
<tbody>
<tr md-data-table-row *ngFor="#material of materials" [selectable-value]="material.id">
<tr *ngFor="#material of materials" md-data-table-selectable-row [selectable-value]="material.id">
<td class="md-text-cell">{{ material.name }}</td>
<td>{{ material.quantity }}</td>
<td>{{ material.price }}</td>
Expand Down
4 changes: 2 additions & 2 deletions ng2-material/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {CONST_EXPR, Type} from "angular2/src/facade/lang";
import {MdAnchor, MdButton} from "./components/button/button";
import {MdCheckbox} from "./components/checkbox/checkbox";
import {MdContent} from "./components/content/content";
import {MdDataTable, MdDataTableHeaderRow, MdDataTableRow} from './components/data_table/data_table';
import {MdDataTable, MdDataTableHeaderSelectableRow, MdDataTableSelectableRow} from './components/data_table/data_table';
import {MdDialog} from "./components/dialog/dialog";
import {MdDivider} from "./components/divider/divider";
import {MdIcon} from "./components/icon/icon";
Expand Down Expand Up @@ -83,7 +83,7 @@ export const MATERIAL_DIRECTIVES: Type[] = CONST_EXPR([
MdAnchor, MdButton,
MdCheckbox,
MdContent,
MdDataTable, MdDataTableHeaderRow, MdDataTableRow,
MdDataTable, MdDataTableHeaderSelectableRow, MdDataTableSelectableRow,
MdDivider,
MdIcon,
MdInk,
Expand Down
32 changes: 26 additions & 6 deletions ng2-material/components/data_table/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,48 @@ MdDataTable is an enhancment of classic data tables.
### Properties
| Name | Target | Type | Description |
| --- | --- | --- | --- |
| selectable | md-data-table | boolean | Enable one checkbox per line and a master checkbox to rule them all. |
| md-data-table-header-row | thead tr | boolean | Enable the master checkbox on header. |
| md-data-table-row | tbody tr | boolean | Enable the checkbox on a row. |
| selectable | md-data-table | boolean | Enable listeners to children's checkbox.

### Events
| Name | Description |
| --- | --- |
| onSelectableChange | Emitted when the user select or unselect a row |

## Selectable header row
### Properties
| Name | Target | Type | Description |
| --- | --- | --- | --- |
| md-data-table-header-selectable-row | thead tr | boolean | Enable the master checkbox on header. |

### Events
| Name | Description |
| --- | --- |
| onChange | Emitted when the user check or uncheck the master checkbox |

## Selectable header row
### Properties
| Name | Target | Type | Description |
| --- | --- | --- | --- |
| md-data-table-selectable-row | tbody tr | boolean | Enable a checkbox for this row. |
| selectable-value | tbody tr | string | value of the checkbox. If it's not set the checkbox's value will be the index of the row. |

### Events
| Name | Description |
| --- | --- |
| selectable_change | Emmited when the user select or unselect a row |
| onChange | Emitted when the user check or uncheck the checkbox |

## Examples
```
<md-data-table [selectable]="true">
<thead>
<tr md-data-table-header-row>
<tr md-data-table-header-selectable-row>
<th class="md-text-cell">Material</th>
<th>Quantity</th>
<th>Unit price</th>
</tr>
</thead>
<tbody>
<tr *ngFor="#material of materials" md-data-table-row [selectable-value]="material.id">
<tr *ngFor="#material of materials" md-data-table-selectable-row [selectable-value]="material.id">
<td class="md-text-cell">{{ material.name }}</td>
<td>{{ material.quantity }}</td>
<td>{{ material.price }}</td>
Expand Down
68 changes: 40 additions & 28 deletions ng2-material/components/data_table/data_table.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {Component, Input, Output, EventEmitter, ContentChildren, QueryList} from "angular2/core";
import {MdDataTableHeaderRow, MdDataTableRow, ITableRow} from "./data_table_tr";
import "rxjs/add/operator/share";
import {Component, Input, Output, EventEmitter, ContentChild, ContentChildren, QueryList, AfterContentInit} from 'angular2/core';
import {isPresent} from "angular2/src/facade/lang";
import 'rxjs/add/operator/share';
import {MdDataTableHeaderSelectableRow, MdDataTableSelectableRow, ITableSelectableRowSelectionChange} from './data_table_selectable_tr';

export * from './data_table_tr';
export * from './data_table_selectable_tr';

/**
* Selectable change event data
Expand All @@ -13,72 +14,83 @@ export interface ITableSelectionChange {
values: any[];
}


@Component({
selector: 'md-data-table',
template: `<ng-content></ng-content>`,
directives: [MdDataTableHeaderRow, MdDataTableRow],
directives: [MdDataTableHeaderSelectableRow, MdDataTableSelectableRow],
host: {
'[class.md-data-table]': 'true',
'[class.md-data-table-selectable]': 'selectable',
}
})
export class MdDataTable {
export class MdDataTable implements AfterContentInit {
@Input()
selectable: boolean;
@Output()
onSelectableAll: EventEmitter<any> = new EventEmitter(false);
@Output()
onSelectableChange: EventEmitter<any> = new EventEmitter(false);

@ContentChildren(MdDataTableRow, true)
_rows: QueryList<MdDataTableRow>;
@ContentChild(MdDataTableHeaderSelectableRow)
_masterRow: MdDataTableHeaderSelectableRow;

@ContentChildren(MdDataTableSelectableRow, true)
_rows: QueryList<MdDataTableSelectableRow>;

selected: Array<string> = [];

constructor() {
this.onSelectableChange.share();
}

/**
* Fill/Empty the array of selected values
*
* @param {MdDataTableRow} tr
*/
toggleActivity(tr: ITableRow) {
let event: ITableSelectionChange = {
change(event: ITableSelectableRowSelectionChange) {
let outputEvent: ITableSelectionChange = {
name: 'selectable_change',
allSelected: false,
values: []
};
if (tr instanceof MdDataTableHeaderRow === true) {
if (tr.isActive === true) {
event.allSelected = true;
event.values = this._getRowsValues();
if (event.target instanceof MdDataTableHeaderSelectableRow === true) {
if (event.isActive === true) {
outputEvent.allSelected = true;
outputEvent.values = this._getRowsValues();
}
} else {
event.values = this.selected.slice(0);
outputEvent.values = this.selected.slice(0);

if (tr.isActive === true) {
event.values.push(tr.selectableValue);
if (event.isActive === true) {
outputEvent.values.push(event.selectableValue);
} else {
let index = event.values.indexOf(tr.selectableValue);
let index = outputEvent.values.indexOf(event.selectableValue);
if (index !== -1) {
event.values.splice(index, 1);
outputEvent.values.splice(index, 1);
}
}
}

// dispatch change
this.selected = event.values;
this.onSelectableChange.emit(event);
this.selected = outputEvent.values;
this.onSelectableChange.emit(outputEvent);
}

/**
* @returns {Array<string>}
*/
_getRowsValues(): any[] {
return this._rows.toArray()
.map((tr: MdDataTableRow) => tr.selectableValue);
.map((tr: MdDataTableSelectableRow) => tr.selectableValue);
}

ngAfterContentInit() {
if (this.selectable === true) {
if (isPresent(this._masterRow)) {
this._masterRow.onChange.subscribe(this.change.bind(this));
}

this._rows.toArray()
.map((tr: MdDataTableSelectableRow) => {
tr.onChange.subscribe(this.change.bind(this));
});
}
}

}
144 changes: 144 additions & 0 deletions ng2-material/components/data_table/data_table_selectable_tr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import {Component, Output, Input, EventEmitter, Inject, Optional, forwardRef, ElementRef, AfterContentInit} from "angular2/core";
import {isPresent} from "angular2/src/facade/lang";
import 'rxjs/add/operator/share';
import 'rxjs/add/operator/map';
import {MdCheckbox} from "../checkbox/checkbox";
import {MdDataTable} from './data_table';

/**
* MdDataTable row
*/
export interface ITableSelectableRow {
selectableValue: string;
onChange: EventEmitter<ITableSelectableRowSelectionChange>;
isActive: boolean;
change: () => void;
ngAfterContentInit: () => void;
}

/**
* Selectable change event data
*/
export interface ITableSelectableRowSelectionChange {
name: string;
target: ITableSelectableRow;
isActive: boolean;
selectableValue: string;
}

export abstract class AbstractMdDataTableSelectableRow implements AfterContentInit, ITableSelectableRow {
@Input('selectable-value')
selectableValue: string;
@Output()
onChange: EventEmitter<ITableSelectableRowSelectionChange> = new EventEmitter(false);
isActive: boolean = false;

constructor(@Optional()
@Inject(forwardRef(() => MdDataTable))
public table: MdDataTable, protected _element: ElementRef) {
this.onChange.share();
}

/**
* Change active status
*/
change() {
this.isActive = !this.isActive;

let event: ITableSelectableRowSelectionChange = {
name: 'selectable_row_change',
target: this,
isActive: this.isActive,
selectableValue: this.selectableValue
}
this.onChange.emit(event);
}

ngAfterContentInit() {}
}

@Component({
selector: 'tr[md-data-table-header-selectable-row]',
template: `
<th>
<md-checkbox #check [checked]="isActive"></md-checkbox>
</th>
<ng-content></ng-content>
`,
directives: [MdCheckbox],
host: {
'[class.active]': 'isActive',
'(click)': 'change()'
}
})
export class MdDataTableHeaderSelectableRow extends AbstractMdDataTableSelectableRow {
constructor(@Optional()
@Inject(forwardRef(() => MdDataTable))
public table: MdDataTable, protected _element: ElementRef) {
super(table, _element);
}

_bindListener(): void {
this.table.onSelectableChange
.map(event => event.allSelected)
.subscribe(newActiveStatus => this.isActive = newActiveStatus);
}

ngAfterContentInit() {
if (isPresent(this.table)) {
this._bindListener();
}
}
}

@Component({
selector: 'tr[md-data-table-selectable-row]',
template: `
<td>
<md-checkbox #check [checked]="isActive"></md-checkbox>
</td>
<ng-content></ng-content>
`,
directives: [MdCheckbox],
host: {
'[class.active]': 'isActive',
'(click)': 'change()'
}
})
export class MdDataTableSelectableRow extends AbstractMdDataTableSelectableRow {
constructor(@Optional()
@Inject(forwardRef(() => MdDataTable))
public table: MdDataTable, protected _element: ElementRef) {
super(table, _element);
}

/**
* @param {any} element
*
* @returns {string}
*/
_getIndex(element): string {
return Array.prototype.indexOf.call(element.parentNode.children, element).toString();
}

_bindListener(): void {
this.table.onSelectableChange
.map(event => {
return event.values !== undefined &&
event.values.length &&
(event.values.findIndex((value: string) => value === this.selectableValue)) !== -1;
})
.subscribe(newActiveStatus => this.isActive = newActiveStatus);
}

ngAfterContentInit() {
let element = this._element.nativeElement;
if (this.selectableValue === undefined) {
this.selectableValue = this._getIndex(element);
}

if (isPresent(this.table)) {
this._bindListener();
}
}
}
Loading

0 comments on commit 115ba65

Please sign in to comment.