[ADF-1115] selection management for DT/DL components (#2100)

* row select/unselect dom events for DT

- new events for datatable
- improved unit tests for empty content placeholders

* improved selection management for DT

* selection management for document list

* fix tests
This commit is contained in:
Denys Vuika
2017-07-19 12:00:03 +01:00
committed by Eugenio Romano
parent 6bde12f770
commit 24bd860d38
13 changed files with 205 additions and 77 deletions

View File

@@ -55,7 +55,10 @@
</td>
<td *ngIf="multiselect">
<md-checkbox [(ngModel)]="row.isSelected"></md-checkbox>
<md-checkbox
[checked]="row.isSelected"
(change)="onCheckboxChange(row, $event)">
</md-checkbox>
</td>
<td *ngFor="let col of data.getColumns()"
class="adf-data-table-cell adf-data-table-cell--{{col.type || 'text'}} {{col.cssClass}}"

View File

@@ -17,7 +17,7 @@
import {
AfterContentInit, Component, ContentChild, DoCheck, ElementRef, EventEmitter, Input,
IterableDiffers, OnChanges, Optional, Output, SimpleChange, SimpleChanges, TemplateRef
IterableDiffers, OnChanges, Output, SimpleChange, SimpleChanges, TemplateRef
} from '@angular/core';
import { MdCheckboxChange } from '@angular/material';
import { AlfrescoTranslationService, DataColumnListComponent } from 'ng2-alfresco-core';
@@ -92,10 +92,11 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck
@Input()
loading: boolean = false;
public noContentTemplate: TemplateRef<any>;
public loadingTemplate: TemplateRef<any>;
noContentTemplate: TemplateRef<any>;
loadingTemplate: TemplateRef<any>;
isSelectAllChecked: boolean = false;
selection = new Array<DataRow>();
private clickObserver: Observer<DataRowEvent>;
private click$: Observable<DataRowEvent>;
@@ -108,7 +109,7 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck
private multiClickStreamSub: Subscription;
constructor(translateService: AlfrescoTranslationService,
@Optional() private el: ElementRef,
private elementRef: ElementRef,
private differs: IterableDiffers) {
if (differs) {
this.differ = differs.find([]).create(null);
@@ -173,10 +174,9 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck
this.singleClickStreamSub = singleClickStream.subscribe((obj: DataRowEvent[]) => {
let event: DataRowEvent = obj[0];
let el = obj[0].sender.el;
this.rowClick.emit(event);
if (!event.defaultPrevented && el.nativeElement) {
el.nativeElement.dispatchEvent(
if (!event.defaultPrevented) {
this.elementRef.nativeElement.dispatchEvent(
new CustomEvent('row-click', {
detail: event,
bubbles: true
@@ -192,10 +192,9 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck
this.multiClickStreamSub = multiClickStream.subscribe((obj: DataRowEvent[]) => {
let event: DataRowEvent = obj[0];
let el = obj[0].sender.el;
this.rowDblClick.emit(event);
if (!event.defaultPrevented && el.nativeElement) {
el.nativeElement.dispatchEvent(
if (!event.defaultPrevented) {
this.elementRef.nativeElement.dispatchEvent(
new CustomEvent('row-dblclick', {
detail: event,
bubbles: true
@@ -247,21 +246,32 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck
const newValue = !row.isSelected;
const rows = this.data.getRows();
const domEventName = newValue ? 'row-select' : 'row-unselect';
const domEvent = new CustomEvent(domEventName, {
detail: {
row: row,
selection: this.selection
},
bubbles: true
});
if (this.isSingleSelectionMode()) {
rows.forEach(r => r.isSelected = false);
row.isSelected = newValue;
this.resetSelection();
this.selectRow(row, newValue);
this.elementRef.nativeElement.dispatchEvent(domEvent);
}
if (this.isMultiSelectionMode()) {
const modifier = e.metaKey || e.ctrlKey;
if (!modifier) {
rows.forEach(r => r.isSelected = false);
this.resetSelection();
}
row.isSelected = newValue;
this.selectRow(row, newValue);
this.elementRef.nativeElement.dispatchEvent(domEvent);
}
}
let dataRowEvent = new DataRowEvent(row, e, this);
const dataRowEvent = new DataRowEvent(row, e, this);
this.clickObserver.next(dataRowEvent);
}
}
@@ -272,7 +282,9 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck
if (rows && rows.length > 0) {
rows.forEach(r => r.isSelected = false);
}
this.selection.splice(0);
}
this.isSelectAllChecked = false;
}
onRowDblClick(row: DataRow, e?: Event) {
@@ -301,12 +313,29 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck
let rows = this.data.getRows();
if (rows && rows.length > 0) {
for (let i = 0; i < rows.length; i++) {
rows[i].isSelected = e.checked;
this.selectRow(rows[i], e.checked);
}
}
}
}
onCheckboxChange(row: DataRow, event: MdCheckboxChange) {
const newValue = event.checked;
this.selectRow(row, newValue);
const domEventName = newValue ? 'row-select' : 'row-unselect';
const domEvent = new CustomEvent(domEventName, {
detail: {
row: row,
selection: this.selection
},
bubbles: true
});
this.elementRef.nativeElement.dispatchEvent(domEvent);
}
onImageLoadingError(event: Event) {
if (event && this.fallbackThumbnail) {
let element = <any> event.target;
@@ -384,4 +413,20 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck
return `${row.cssClass} ${this.rowStyleClass}`;
}
private selectRow(row: DataRow, value: boolean) {
if (row) {
row.isSelected = value;
const idx = this.selection.indexOf(row);
if (value) {
if (idx < 0) {
this.selection.push(row);
}
} else {
if (idx > -1) {
this.selection.splice(idx, 1);
}
}
}
}
}

View File

@@ -15,31 +15,42 @@
* limitations under the License.
*/
import { Injector } from '@angular/core';
import { getTestBed, TestBed } from '@angular/core/testing';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CoreModule } from 'ng2-alfresco-core';
import { DataTableCellComponent } from '../components/datatable/datatable-cell.component';
import { DataTableComponent } from '../components/datatable/datatable.component';
import { MaterialModule } from '../material.module';
import { LoadingContentTemplateDirective } from './loading-template.directive';
describe('LoadingContentTemplateDirective', () => {
let injector: Injector;
let loadingContentTemplateDirective: LoadingContentTemplateDirective;
beforeEach(() => {
let dataTable: DataTableComponent;
let directive: LoadingContentTemplateDirective;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
MaterialModule,
CoreModule.forRoot()
],
providers: [
LoadingContentTemplateDirective,
DataTableComponent
declarations: [
DataTableComponent,
DataTableCellComponent,
LoadingContentTemplateDirective
]
});
injector = getTestBed();
loadingContentTemplateDirective = injector.get(LoadingContentTemplateDirective);
}).compileComponents();
}));
beforeEach(() => {
let fixture = TestBed.createComponent(DataTableComponent);
dataTable = fixture.componentInstance;
directive = new LoadingContentTemplateDirective(dataTable);
});
it('is defined', () => {
expect(loadingContentTemplateDirective).toBeDefined();
it('applies template to the datatable', () => {
const template = {};
directive.template = template;
directive.ngAfterContentInit();
expect(dataTable.loadingTemplate).toBe(template);
});
});

View File

@@ -30,7 +30,9 @@ export class LoadingContentTemplateDirective implements AfterContentInit {
}
ngAfterContentInit() {
this.dataTable.loadingTemplate = this.template;
if (this.dataTable) {
this.dataTable.loadingTemplate = this.template;
}
}
}

View File

@@ -15,31 +15,42 @@
* limitations under the License.
*/
import { Injector } from '@angular/core';
import { getTestBed, TestBed } from '@angular/core/testing';
import { async, getTestBed, TestBed } from '@angular/core/testing';
import { CoreModule } from 'ng2-alfresco-core';
import { DataTableCellComponent } from '../components/datatable/datatable-cell.component';
import { DataTableComponent } from '../components/datatable/datatable.component';
import { MaterialModule } from '../material.module';
import { NoContentTemplateDirective } from './no-content-template.directive';
describe('NoContentTemplateDirective', () => {
let injector: Injector;
let noContentTemplateDirective: NoContentTemplateDirective;
beforeEach(() => {
let dataTable: DataTableComponent;
let directive: NoContentTemplateDirective;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
MaterialModule,
CoreModule.forRoot()
],
providers: [
NoContentTemplateDirective,
DataTableComponent
declarations: [
DataTableComponent,
DataTableCellComponent,
NoContentTemplateDirective
]
});
injector = getTestBed();
noContentTemplateDirective = injector.get(NoContentTemplateDirective);
}).compileComponents();
}));
beforeEach(() => {
let fixture = TestBed.createComponent(DataTableComponent);
dataTable = fixture.componentInstance;
directive = new NoContentTemplateDirective(dataTable);
});
it('is defined', () => {
expect(noContentTemplateDirective).toBeDefined();
it('applies template to the datatable', () => {
const template = {};
directive.template = template;
directive.ngAfterContentInit();
expect(dataTable.noContentTemplate).toBe(template);
});
});

View File

@@ -30,6 +30,8 @@ export class NoContentTemplateDirective implements AfterContentInit {
}
ngAfterContentInit() {
this.dataTable.noContentTemplate = this.template;
if (this.dataTable) {
this.dataTable.noContentTemplate = this.template;
}
}
}