[ADF-2541] reset datatable selection when rows are changed from code (#3410)

* reset selection when rows are replaced from code

* code fixes

* unit test updates

* visualise selection count for testing purposes

* make row selection api public

* remove question mark from the event name
This commit is contained in:
Denys Vuika
2018-05-30 11:53:12 +01:00
committed by Eugenio Romano
parent 7e59b24f2c
commit 3162a73f61
8 changed files with 98 additions and 64 deletions

View File

@@ -1,30 +1,34 @@
<div class="p-10"> <adf-datatable
<adf-datatable #dataTable
[data]="data" [data]="data"
[selectionMode]="selectionMode" [selectionMode]="selectionMode"
[multiselect]="multiselect" [multiselect]="multiselect"
[actions]="true" [actions]="true"
rowStyleClass="custom-row-style" rowStyleClass="custom-row-style"
(showRowActionsMenu)="onShowRowActionsMenu($event)" (showRowActionsMenu)="onShowRowActionsMenu($event)"
(executeRowAction)="onExecuteRowAction($event)" (executeRowAction)="onExecuteRowAction($event)"
(row-click)="onRowClick($event)" (row-click)="onRowClick($event)"
(row-dblclick)="onRowDblClick($event)"> (row-dblclick)="onRowDblClick($event)">
<!-- HTML column definition demo --> <!-- HTML column definition demo -->
<!-- <!--
<data-columns> <data-columns>
<data-column type="image" key="icon" [sortable]="false"></data-column> <data-column type="image" key="icon" [sortable]="false"></data-column>
<data-column key="id" title="Id"></data-column> <data-column key="id" title="Id"></data-column>
<data-column key="createdOn" title="Created"></data-column> <data-column key="createdOn" title="Created"></data-column>
<data-column key="name" title="Name" class="full-width name-column"></data-column> <data-column key="name" title="Name" class="full-width name-column"></data-column>
<data-column key="createdBy.name" title="Created By"></data-column> <data-column key="createdBy.name" title="Created By"></data-column>
</data-columns> </data-columns>
--> -->
</adf-datatable> </adf-datatable>
<div>
Selected items: {{ dataTable.selection?.length }}
</div> </div>
<div class="p-10" data-automation-id="multiselect">
<div data-automation-id="multiselect">
<mat-checkbox [(ngModel)]="multiselect">{{ 'DATATABLE.MULTISELECT'| translate }}</mat-checkbox> <mat-checkbox [(ngModel)]="multiselect">{{ 'DATATABLE.MULTISELECT'| translate }}</mat-checkbox>
</div> </div>
<div class="p-10"> <div>
<p>{{ 'DATATABLE.MULTISELECT_DESCRIPTION'| translate }}</p> <p>{{ 'DATATABLE.MULTISELECT_DESCRIPTION'| translate }}</p>
<mat-form-field> <mat-form-field>
<mat-select placeholder="Selection Mode" [(ngModel)]="selectionMode" name="food"> <mat-select placeholder="Selection Mode" [(ngModel)]="selectionMode" name="food">
@@ -34,7 +38,7 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="p-10"> <div>
<button mat-raised-button (click)="reset()">{{ 'DATATABLE.RESET_DEFAULT'| translate }}</button> <button mat-raised-button (click)="reset()">{{ 'DATATABLE.RESET_DEFAULT'| translate }}</button>
<button mat-raised-button (click)="addRow()">{{ 'DATATABLE.ADD_ROW'| translate }}</button> <button mat-raised-button (click)="addRow()">{{ 'DATATABLE.ADD_ROW'| translate }}</button>
<button mat-raised-button (click)="replaceRows()">{{ 'DATATABLE.REPLACE_ROWS'| translate }}</button> <button mat-raised-button (click)="replaceRows()">{{ 'DATATABLE.REPLACE_ROWS'| translate }}</button>

View File

@@ -1,16 +0,0 @@
adf-datatable ::ng-deep .custom-row-style.alfresco-datatable__row:focus {
outline-offset: -1px;
outline-width: 1px;
outline-color: green;
outline-style: solid;
}
adf-datatable ::ng-deep .custom-row-style.alfresco-datatable__row--selected {
color: green;
}
adf-datatable ::ng-deep table {
max-width: 100%;
overflow: hidden;
white-space: normal !important;
}

View File

@@ -22,8 +22,7 @@ import { Observable } from 'rxjs/Observable';
@Component({ @Component({
selector: 'app-datatable', selector: 'app-datatable',
templateUrl: './datatable.component.html', templateUrl: './datatable.component.html'
styleUrls: ['./datatable.component.scss']
}) })
export class DataTableComponent { export class DataTableComponent {
@@ -41,8 +40,8 @@ export class DataTableComponent {
private _imageUrl = 'http://placehold.it/140x100'; private _imageUrl = 'http://placehold.it/140x100';
private _createdBy: any = { private _createdBy: any = {
name: 'Denys Vuika', name: 'Administrator',
email: 'denys.vuika@alfresco.com' email: 'admin@alfresco.com'
}; };
constructor(private apiService: AlfrescoApiService, private logService: LogService) { constructor(private apiService: AlfrescoApiService, private logService: LogService) {

View File

@@ -14,6 +14,12 @@ and [Tasklist](../process-services/task-list.component.md) components.
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| selectedRow | DataRow | The data for the currently selected row. | | selectedRow | DataRow | The data for the currently selected row. |
## Events
| Name | Type | Description |
| ---- | ---- | ----------- |
| rowsChanged | `Subject<Array<DataRow>>` | Raised when data adapter gets new rows. |
## Methods ## Methods
[`getRows(): Array<DataRow>;`](../../lib/core/datatable/data/data-row.model.ts)<br/> [`getRows(): Array<DataRow>;`](../../lib/core/datatable/data/data-row.model.ts)<br/>
@@ -27,11 +33,11 @@ Get/set an array of column specifications.
`getValue(row: DataRow, col: DataColumn): any;`<br/> `getValue(row: DataRow, col: DataColumn): any;`<br/>
Get the data value from a specific table cell. Get the data value from a specific table cell.
`getSorting(): DataSorting;` `getSorting(): DataSorting;`<br/>
`setSorting(sorting: DataSorting): void;` `setSorting(sorting: DataSorting): void;`<br/>
Get/set the sorting key and direction (ascending or descending). Get/set the sorting key and direction (ascending or descending).
`sort(key?: string, direction?: string): void;` `sort(key?: string, direction?: string): void;`<br/>
Sort the table with a specified key and direction (ascending or descending). Sort the table with a specified key and direction (ascending or descending).
## Details ## Details

View File

@@ -484,15 +484,6 @@ describe('DataTable', () => {
expect(table.data).toEqual(jasmine.any(ObjectDataTableAdapter)); expect(table.data).toEqual(jasmine.any(ObjectDataTableAdapter));
}); });
it('should load data table on onChange', () => {
let table = new DataTableComponent(null, null);
let data = new ObjectDataTableAdapter([], []);
expect(table.data).toBeUndefined();
table.ngOnChanges({'data': new SimpleChange('123', data, true)});
expect(table.data).toEqual(data);
});
it('should initialize with custom data', () => { it('should initialize with custom data', () => {
let data = new ObjectDataTableAdapter([], []); let data = new ObjectDataTableAdapter([], []);
dataTable.data = data; dataTable.data = data;
@@ -690,6 +681,22 @@ describe('DataTable', () => {
expect(dataTable.isSelectAllChecked).toBeFalsy(); expect(dataTable.isSelectAllChecked).toBeFalsy();
}); });
it('should reset selection upon data rows change', () => {
let data = new ObjectDataTableAdapter([{}, {}, {}], []);
dataTable.data = data;
dataTable.multiselect = true;
dataTable.ngAfterContentInit();
dataTable.onSelectAllClick(<MatCheckboxChange> { checked: true });
expect(dataTable.selection.every(entry => entry.isSelected));
data.setRows([]);
fixture.detectChanges();
expect(dataTable.selection.every(entry => !entry.isSelected));
});
it('should update rows on "select all" click', () => { it('should update rows on "select all" click', () => {
let data = new ObjectDataTableAdapter([{}, {}, {}], []); let data = new ObjectDataTableAdapter([{}, {}, {}], []);
let rows = data.getRows(); let rows = data.getRows();

View File

@@ -20,9 +20,7 @@ import {
IterableDiffers, OnChanges, Output, SimpleChange, SimpleChanges, TemplateRef, ViewEncapsulation, OnDestroy IterableDiffers, OnChanges, Output, SimpleChange, SimpleChanges, TemplateRef, ViewEncapsulation, OnDestroy
} from '@angular/core'; } from '@angular/core';
import { MatCheckboxChange } from '@angular/material'; import { MatCheckboxChange } from '@angular/material';
import { Subscription } from 'rxjs/Subscription'; import { Subscription, Observable, Observer } from 'rxjs/Rx';
import { Observable } from 'rxjs/Observable';
import { Observer } from 'rxjs/Observer';
import { DataColumnListComponent } from '../../../data-column/data-column-list.component'; import { DataColumnListComponent } from '../../../data-column/data-column-list.component';
import { DataColumn } from '../../data/data-column.model'; import { DataColumn } from '../../data/data-column.model';
import { DataRowEvent } from '../../data/data-row-event.model'; import { DataRowEvent } from '../../data/data-row-event.model';
@@ -168,6 +166,7 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
private subscriptions: Subscription[] = []; private subscriptions: Subscription[] = [];
private singleClickStreamSub: Subscription; private singleClickStreamSub: Subscription;
private multiClickStreamSub: Subscription; private multiClickStreamSub: Subscription;
private dataRowsChanged: Subscription;
constructor(private elementRef: ElementRef, constructor(private elementRef: ElementRef,
differs: IterableDiffers, differs: IterableDiffers,
@@ -196,6 +195,7 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
this.initTable(); this.initTable();
} else { } else {
this.data = changes['data'].currentValue; this.data = changes['data'].currentValue;
this.setupData(this.data);
} }
return; return;
} }
@@ -205,6 +205,7 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
this.initTable(); this.initTable();
} else { } else {
this.setTableRows(changes['rows'].currentValue); this.setTableRows(changes['rows'].currentValue);
this.setupData(this.data);
} }
return; return;
} }
@@ -293,15 +294,34 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
private initTable() { private initTable() {
this.data = new ObjectDataTableAdapter(this.rows, this.schema); this.data = new ObjectDataTableAdapter(this.rows, this.schema);
this.setupData(this.data);
this.rowMenuCache = {}; this.rowMenuCache = {};
} }
private setupData(adapter: DataTableAdapter) {
if (this.dataRowsChanged) {
this.dataRowsChanged.unsubscribe();
this.dataRowsChanged = null;
}
this.resetSelection();
if (adapter && adapter.rowsChanged) {
this.dataRowsChanged = adapter.rowsChanged.subscribe(() => {
this.resetSelection();
});
}
}
isTableEmpty() { isTableEmpty() {
return this.data === undefined || this.data === null; return this.data === undefined || this.data === null;
} }
private setTableRows(rows) { private setTableRows(rows: any[]) {
if (this.data) { if (this.data) {
if (rows && rows.length > 0) {
this.resetSelection();
}
this.data.setRows(this.convertToRowsData(rows)); this.data.setRows(this.convertToRowsData(rows));
} }
} }
@@ -560,7 +580,7 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
} }
} }
private selectRow(row: DataRow, value: boolean) { selectRow(row: DataRow, value: boolean) {
if (row) { if (row) {
row.isSelected = value; row.isSelected = value;
const idx = this.selection.indexOf(row); const idx = this.selection.indexOf(row);
@@ -588,8 +608,8 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
} }
getSortableColumns() { getSortableColumns() {
return this.data.getColumns().filter((currentColum) => { return this.data.getColumns().filter(column => {
return currentColum.sortable === true; return column.sortable === true;
}); });
} }
@@ -625,7 +645,13 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
ngOnDestroy() { ngOnDestroy() {
this.unsubscribeClickStream(); this.unsubscribeClickStream();
this.subscriptions.forEach(s => s.unsubscribe()); this.subscriptions.forEach(s => s.unsubscribe());
this.subscriptions = []; this.subscriptions = [];
if (this.dataRowsChanged) {
this.dataRowsChanged.unsubscribe();
this.dataRowsChanged = null;
}
} }
} }

View File

@@ -18,8 +18,11 @@
import { DataColumn } from './data-column.model'; import { DataColumn } from './data-column.model';
import { DataRow } from './data-row.model'; import { DataRow } from './data-row.model';
import { DataSorting } from './data-sorting.model'; import { DataSorting } from './data-sorting.model';
import { Subject } from 'rxjs/Rx';
export interface DataTableAdapter { export interface DataTableAdapter {
rowsChanged?: Subject<Array<DataRow>>;
selectedRow: DataRow; selectedRow: DataRow;
getRows(): Array<DataRow>; getRows(): Array<DataRow>;
setRows(rows: Array<DataRow>): void; setRows(rows: Array<DataRow>): void;

View File

@@ -21,6 +21,7 @@ import { ObjectDataRow } from './object-datarow.model';
import { ObjectDataColumn } from './object-datacolumn.model'; import { ObjectDataColumn } from './object-datacolumn.model';
import { DataSorting } from './data-sorting.model'; import { DataSorting } from './data-sorting.model';
import { DataTableAdapter } from './datatable-adapter'; import { DataTableAdapter } from './datatable-adapter';
import { Subject } from 'rxjs/Subject';
// Simple implementation of the DataTableAdapter interface. // Simple implementation of the DataTableAdapter interface.
export class ObjectDataTableAdapter implements DataTableAdapter { export class ObjectDataTableAdapter implements DataTableAdapter {
@@ -30,6 +31,7 @@ export class ObjectDataTableAdapter implements DataTableAdapter {
private _columns: DataColumn[]; private _columns: DataColumn[];
selectedRow: DataRow; selectedRow: DataRow;
rowsChanged: Subject<Array<DataRow>>;
static generateSchema(data: any[]) { static generateSchema(data: any[]) {
let schema = []; let schema = [];
@@ -75,6 +77,8 @@ export class ObjectDataTableAdapter implements DataTableAdapter {
this.sort(sortable[0].key, 'asc'); this.sort(sortable[0].key, 'asc');
} }
} }
this.rowsChanged = new Subject<Array<DataRow>>();
} }
getRows(): Array<DataRow> { getRows(): Array<DataRow> {
@@ -84,6 +88,7 @@ export class ObjectDataTableAdapter implements DataTableAdapter {
setRows(rows: Array<DataRow>) { setRows(rows: Array<DataRow>) {
this._rows = rows || []; this._rows = rows || [];
this.sort(); this.sort();
this.rowsChanged.next(this._rows);
} }
getColumns(): Array<DataColumn> { getColumns(): Array<DataColumn> {