diff --git a/demo-shell-ng2/app/components/files/files.component.ts b/demo-shell-ng2/app/components/files/files.component.ts
index ffd4d3f177..b01780e294 100644
--- a/demo-shell-ng2/app/components/files/files.component.ts
+++ b/demo-shell-ng2/app/components/files/files.component.ts
@@ -41,6 +41,18 @@ export class FilesComponent implements OnInit {
useCustomToolbar = true;
useDropdownBreadcrumb = true;
+ selectionModes = [
+ { value: 'none', viewValue: 'None' },
+ { value: 'single', viewValue: 'Single' },
+ { value: 'multiple', viewValue: 'Multiple' }
+ ];
+
+ @Input()
+ selectionMode = 'multiple';
+
+ @Input()
+ multiselect = false;
+
@Input()
multipleFileUpload: boolean = false;
diff --git a/ng2-components/ng2-alfresco-datatable/README.md b/ng2-components/ng2-alfresco-datatable/README.md
index 468e3117ff..22c0c562d1 100644
--- a/ng2-components/ng2-alfresco-datatable/README.md
+++ b/ng2-components/ng2-alfresco-datatable/README.md
@@ -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
diff --git a/ng2-components/ng2-alfresco-datatable/src/components/datatable/datatable.component.html b/ng2-components/ng2-alfresco-datatable/src/components/datatable/datatable.component.html
index 5b22d92ace..a5c3ffcd1f 100644
--- a/ng2-components/ng2-alfresco-datatable/src/components/datatable/datatable.component.html
+++ b/ng2-components/ng2-alfresco-datatable/src/components/datatable/datatable.component.html
@@ -55,7 +55,10 @@
-
+
+
|
;
- public loadingTemplate: TemplateRef;
+ noContentTemplate: TemplateRef;
+ loadingTemplate: TemplateRef;
isSelectAllChecked: boolean = false;
+ selection = new Array();
private clickObserver: Observer;
private click$: Observable;
@@ -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 = 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);
+ }
+ }
+ }
+ }
}
diff --git a/ng2-components/ng2-alfresco-datatable/src/directives/loading-template.directive.spec.ts b/ng2-components/ng2-alfresco-datatable/src/directives/loading-template.directive.spec.ts
index c815b33e4f..d377e2622c 100644
--- a/ng2-components/ng2-alfresco-datatable/src/directives/loading-template.directive.spec.ts
+++ b/ng2-components/ng2-alfresco-datatable/src/directives/loading-template.directive.spec.ts
@@ -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);
});
});
diff --git a/ng2-components/ng2-alfresco-datatable/src/directives/loading-template.directive.ts b/ng2-components/ng2-alfresco-datatable/src/directives/loading-template.directive.ts
index 242f7864aa..720c7739ae 100644
--- a/ng2-components/ng2-alfresco-datatable/src/directives/loading-template.directive.ts
+++ b/ng2-components/ng2-alfresco-datatable/src/directives/loading-template.directive.ts
@@ -30,7 +30,9 @@ export class LoadingContentTemplateDirective implements AfterContentInit {
}
ngAfterContentInit() {
- this.dataTable.loadingTemplate = this.template;
+ if (this.dataTable) {
+ this.dataTable.loadingTemplate = this.template;
+ }
}
}
diff --git a/ng2-components/ng2-alfresco-datatable/src/directives/no-content-template.directive.spec.ts b/ng2-components/ng2-alfresco-datatable/src/directives/no-content-template.directive.spec.ts
index be28055e8b..d53a282a2f 100644
--- a/ng2-components/ng2-alfresco-datatable/src/directives/no-content-template.directive.spec.ts
+++ b/ng2-components/ng2-alfresco-datatable/src/directives/no-content-template.directive.spec.ts
@@ -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);
});
});
diff --git a/ng2-components/ng2-alfresco-datatable/src/directives/no-content-template.directive.ts b/ng2-components/ng2-alfresco-datatable/src/directives/no-content-template.directive.ts
index 539ed82490..2645bcf47b 100644
--- a/ng2-components/ng2-alfresco-datatable/src/directives/no-content-template.directive.ts
+++ b/ng2-components/ng2-alfresco-datatable/src/directives/no-content-template.directive.ts
@@ -30,6 +30,8 @@ export class NoContentTemplateDirective implements AfterContentInit {
}
ngAfterContentInit() {
- this.dataTable.noContentTemplate = this.template;
+ if (this.dataTable) {
+ this.dataTable.noContentTemplate = this.template;
+ }
}
}
diff --git a/ng2-components/ng2-alfresco-documentlist/README.md b/ng2-components/ng2-alfresco-documentlist/README.md
index 136caf64b4..44a1044e5f 100644
--- a/ng2-components/ng2-alfresco-documentlist/README.md
+++ b/ng2-components/ng2-alfresco-documentlist/README.md
@@ -89,6 +89,7 @@ The properties currentFolderId, folderNode and node are the entry initialization
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| selectionMode | string | 'single' | Row selection mode. Can be none, `single` or `multiple`. For `multiple` mode you can use Cmd (macOS) or Ctrl (Win) modifier key to toggle selection for multiple rows. |
+| selection | Array | [] | Contains selected nodes |
| rowStyle | string | | The inline style to apply to every row, see [NgStyle](https://angular.io/docs/ts/latest/api/common/index/NgStyle-directive.html) docs for more details and usage examples |
| rowStyleClass | string | | The CSS class to apply to every row |
| currentFolderId | string | null | Initial node ID of displayed folder. Can be `-root-`, `-shared-`, `-my-`, or a fixed node ID |
@@ -132,6 +133,8 @@ All of them are `bubbling`, meaning you can handle them in any component up the
| --- | --- |
| node-click | Raised when user clicks the node |
| node-dblclick | Raised when user double-clicks the node |
+| node-select | Raised when user selects a node |
+| node-unselect | Raised when user unselects a node |
Every event is represented by a [CustomEvent](https://developer.mozilla.org/en/docs/Web/API/CustomEvent) instance, having at least the following properties as part of the `Event.detail` property value:
@@ -142,6 +145,8 @@ Every event is represented by a [CustomEvent](https://developer.mozilla.org/en/d
}
```
+Please refer to the DataTable documentation to find details about additional DOM events the DocumentList component bubbles up from the DataTable.
+
### Handling DOM events
Here's a basic example on handling DOM events in the parent elements:
diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.html b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.html
index 119ec59589..4c4b76b02d 100644
--- a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.html
+++ b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.html
@@ -20,8 +20,10 @@
(showRowContextMenu)="onShowRowContextMenu($event)"
(showRowActionsMenu)="onShowRowActionsMenu($event)"
(executeRowAction)="onExecuteRowAction($event)"
- (rowClick)="onRowClick($event)"
- (rowDblClick)="onRowDblClick($event)">
+ (rowClick)="onNodeClick($event.value?.node)"
+ (rowDblClick)="onNodeDblClick($event.value?.node)"
+ (row-select)="onNodeSelect($event.detail)"
+ (row-unselect)="onNodeUnselect($event.detail)">
diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.spec.ts
index 3855f8bd6f..faa77a2028 100644
--- a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.spec.ts
+++ b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.spec.ts
@@ -748,47 +748,43 @@ describe('DocumentList', () => {
it('should emit [nodeClick] event on row click', () => {
let node = new NodeMinimalEntry();
let row = new ShareDataRow(node, null, null);
- let event = new DataRowEvent(row, null);
spyOn(documentList, 'onNodeClick').and.callThrough();
- documentList.onRowClick(event);
+ documentList.onNodeClick(node);
expect(documentList.onNodeClick).toHaveBeenCalledWith(node);
});
it('should emit node-click DOM event', (done) => {
let node = new NodeMinimalEntry();
let row = new ShareDataRow(node, null, null);
- let event = new DataRowEvent(row, null);
const htmlElement = fixture.debugElement.nativeElement as HTMLElement;
htmlElement.addEventListener('node-click', (e: CustomEvent) => {
done();
});
- documentList.onRowClick(event);
+ documentList.onNodeClick(node);
});
it('should emit [nodeDblClick] event on row double-click', () => {
let node = new NodeMinimalEntry();
let row = new ShareDataRow(node, null, null);
- let event = new DataRowEvent(row, null);
spyOn(documentList, 'onNodeDblClick').and.callThrough();
- documentList.onRowDblClick(event);
+ documentList.onNodeDblClick(node);
expect(documentList.onNodeDblClick).toHaveBeenCalledWith(node);
});
it('should emit node-dblclick DOM event', (done) => {
let node = new NodeMinimalEntry();
let row = new ShareDataRow(node, null, null);
- let event = new DataRowEvent(row, null);
const htmlElement = fixture.debugElement.nativeElement as HTMLElement;
htmlElement.addEventListener('node-dblclick', (e: CustomEvent) => {
done();
});
- documentList.onRowDblClick(event);
+ documentList.onNodeDblClick(node);
});
it('should load folder by ID on init', () => {
diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.ts b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.ts
index 6c684c8429..d71cf93b89 100644
--- a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.ts
+++ b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.ts
@@ -21,7 +21,7 @@ import {
} from '@angular/core';
import { MinimalNodeEntity, MinimalNodeEntryEntity, NodePaging, Pagination } from 'alfresco-js-api';
import { AlfrescoTranslationService, DataColumnListComponent } from 'ng2-alfresco-core';
-import { DataCellEvent, DataColumn, DataRowActionEvent, DataRowEvent, DataSorting, DataTableComponent, ObjectDataColumn } from 'ng2-alfresco-datatable';
+import { DataCellEvent, DataColumn, DataRow, DataRowActionEvent, DataRowEvent, DataSorting, DataTableComponent, ObjectDataColumn } from 'ng2-alfresco-datatable';
import { Observable, Subject } from 'rxjs/Rx';
import { ImageResolver, RowFilter, ShareDataRow, ShareDataTableAdapter } from './../data/share-datatable-adapter';
import { ContentActionModel } from './../models/content-action.model';
@@ -98,8 +98,8 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
@Input()
loading: boolean = false;
+ selection = new Array();
skipCount: number = 0;
-
pagination: Pagination;
@Input()
@@ -166,7 +166,7 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
constructor(private documentListService: DocumentListService,
private ngZone: NgZone,
translateService: AlfrescoTranslationService,
- private el: ElementRef) {
+ private elementRef: ElementRef) {
if (translateService) {
translateService.addTranslationFolder('ng2-alfresco-documentlist', 'assets/ng2-alfresco-documentlist');
@@ -390,11 +390,10 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
val => {
if (this.isCurrentPageEmpty(val, skipCount)) {
this.updateSkipCount(skipCount - maxItems);
- this.loadFolderNodesByFolderNodeId(id, maxItems, skipCount - maxItems).then(() => {
- resolve(true);
- }, (error) => {
- reject(error);
- });
+ this.loadFolderNodesByFolderNodeId(id, maxItems, skipCount - maxItems).then(
+ () => resolve(true),
+ error => reject(error)
+ );
} else {
this.data.loadPage( val);
this.pagination = val.list.pagination;
@@ -458,7 +457,7 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
},
bubbles: true
});
- this.el.nativeElement.dispatchEvent(domEvent);
+ this.elementRef.nativeElement.dispatchEvent(domEvent);
const event = new NodeEntityEvent(node);
this.nodeClick.emit(event);
@@ -478,11 +477,6 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
}
}
- onRowClick(event: DataRowEvent) {
- let item = ( event.value).node;
- this.onNodeClick(item);
- }
-
onNodeDblClick(node: MinimalNodeEntity) {
const domEvent = new CustomEvent('node-dblclick', {
detail: {
@@ -491,7 +485,7 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
},
bubbles: true
});
- this.el.nativeElement.dispatchEvent(domEvent);
+ this.elementRef.nativeElement.dispatchEvent(domEvent);
const event = new NodeEntityEvent(node);
this.nodeDblClick.emit(event);
@@ -511,9 +505,26 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
}
}
- onRowDblClick(event?: DataRowEvent) {
- let item = ( event.value).node;
- this.onNodeDblClick(item);
+ onNodeSelect(event: { row: ShareDataRow, selection: Array }) {
+ this.selection = event.selection.map(entry => entry.node);
+ const domEvent = new CustomEvent('node-select', {
+ detail: {
+ node: event.row.node,
+ selection: this.selection
+ }
+ });
+ this.elementRef.nativeElement.dispatchEvent(domEvent);
+ }
+
+ onNodeUnselect(event: { row: ShareDataRow, selection: Array }) {
+ this.selection = event.selection.map(entry => entry.node);
+ const domEvent = new CustomEvent('node-unselect', {
+ detail: {
+ node: event.row.node,
+ selection: this.selection
+ }
+ });
+ this.elementRef.nativeElement.dispatchEvent(domEvent);
}
onShowRowContextMenu(event: DataCellEvent) {
|