mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[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:
committed by
Eugenio Romano
parent
6bde12f770
commit
24bd860d38
@@ -161,6 +161,7 @@ export class DataTableDemo {
|
||||
| allowDropFiles | boolean | false | Toggle file drop support for rows (see **ng2-alfresco-core/UploadDirective** for more details) |
|
||||
| loading | boolean | false | Flag that indicate if the datable is in loading state and need to show the loading template. Read the documentation above to know how to configure a loading template |
|
||||
| showHeader | boolean | true | Toggles header visibility |
|
||||
| selection | DataRow[] | [] | Contains selected rows |
|
||||
|
||||
### DataColumn Properties
|
||||
|
||||
@@ -189,14 +190,17 @@ Here's the list of available properties you can define for a Data Column definit
|
||||
|
||||
### DataTable DOM Events
|
||||
|
||||
Below are the DOM events raised by DataTable component.
|
||||
Below are the DOM events raised by DataTable component.
|
||||
These events bubble up the component tree and can be handled by any parent component.
|
||||
|
||||
| Name | Description |
|
||||
| --- | --- |
|
||||
| row-click | Emitted when user clicks the row |
|
||||
| row-dblclick | Emitted when user double-clicks the row |
|
||||
| row-click | Raised when user clicks a row |
|
||||
| row-dblclick | Raised when user double-clicks a row |
|
||||
| row-select | Raised after user selects a row |
|
||||
| row-unselect | Raised after user unselects a row |
|
||||
|
||||
These events are bubbled up the element tree and can be subscribed to from within parent components.
|
||||
For example:
|
||||
|
||||
```html
|
||||
<root-component (row-click)="onRowClick($event)">
|
||||
|
@@ -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}}"
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
});
|
||||
});
|
||||
|
@@ -30,7 +30,9 @@ export class LoadingContentTemplateDirective implements AfterContentInit {
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
this.dataTable.loadingTemplate = this.template;
|
||||
if (this.dataTable) {
|
||||
this.dataTable.loadingTemplate = this.template;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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);
|
||||
});
|
||||
});
|
||||
|
@@ -30,6 +30,8 @@ export class NoContentTemplateDirective implements AfterContentInit {
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
this.dataTable.noContentTemplate = this.template;
|
||||
if (this.dataTable) {
|
||||
this.dataTable.noContentTemplate = this.template;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user