2019-03-21 16:16:19 +01:00
|
|
|
import { MatTableDataSource, MatTable, MatSort, MatPaginator, MatSnackBar, PageEvent } from '@angular/material';
|
2018-09-11 16:38:23 +02:00
|
|
|
import { Title } from '@angular/platform-browser';
|
|
|
|
import { TranslateService } from '@ngx-translate/core';
|
2019-03-21 16:16:19 +01:00
|
|
|
import { ViewChild, Type, OnDestroy } from '@angular/core';
|
2018-10-22 16:44:18 +02:00
|
|
|
|
2018-11-02 11:41:03 +01:00
|
|
|
import { BaseViewComponent } from './base-view';
|
2018-10-22 16:44:18 +02:00
|
|
|
import { BaseViewModel } from './base-view-model';
|
2019-02-18 13:31:15 +01:00
|
|
|
import { BaseSortListService } from 'app/core/ui-services/base-sort-list.service';
|
|
|
|
import { BaseFilterListService } from 'app/core/ui-services/base-filter-list.service';
|
|
|
|
import { BaseModel } from 'app/shared/models/base/base-model';
|
2019-03-21 16:16:19 +01:00
|
|
|
import { ActivatedRoute } from '@angular/router';
|
|
|
|
import { StorageService } from 'app/core/core-services/storage.service';
|
2018-09-11 16:38:23 +02:00
|
|
|
|
2019-03-21 16:16:19 +01:00
|
|
|
export abstract class ListViewBaseComponent<V extends BaseViewModel, M extends BaseModel> extends BaseViewComponent
|
|
|
|
implements OnDestroy {
|
2018-09-11 16:38:23 +02:00
|
|
|
/**
|
2018-12-12 18:06:06 +01:00
|
|
|
* The data source for a table. Requires to be initialized with a BaseViewModel
|
2018-09-11 16:38:23 +02:00
|
|
|
*/
|
|
|
|
public dataSource: MatTableDataSource<V>;
|
|
|
|
|
2018-11-05 17:40:32 +01:00
|
|
|
/**
|
|
|
|
* Toggle for enabling the multiSelect mode. Defaults to false (inactive)
|
|
|
|
*/
|
|
|
|
protected canMultiSelect = false;
|
|
|
|
|
|
|
|
/**
|
2018-12-12 18:06:06 +01:00
|
|
|
* Current state of the multi select mode. TODO Could be merged with edit mode?
|
2018-11-05 17:40:32 +01:00
|
|
|
*/
|
2018-12-12 18:06:06 +01:00
|
|
|
private _multiSelectMode = false;
|
2018-11-05 17:40:32 +01:00
|
|
|
|
|
|
|
/**
|
2018-12-12 18:06:06 +01:00
|
|
|
* An array of currently selected items, upon which multi select actions can be performed
|
2018-11-05 17:40:32 +01:00
|
|
|
* see {@link selectItem}.
|
|
|
|
*/
|
|
|
|
public selectedRows: V[];
|
|
|
|
|
2019-03-21 16:16:19 +01:00
|
|
|
/**
|
|
|
|
* Holds the key for the storage.
|
|
|
|
* This is by default the component's name.
|
|
|
|
*/
|
2019-03-25 12:56:16 +01:00
|
|
|
private paginationStorageKey: string;
|
2019-03-21 16:16:19 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Holds the value from local storage with the 'Paginator' key.
|
|
|
|
*/
|
2019-03-25 12:56:16 +01:00
|
|
|
private paginationStorageObject: { [key: string]: number };
|
2019-03-21 16:16:19 +01:00
|
|
|
|
2019-04-04 11:46:30 +02:00
|
|
|
/**
|
|
|
|
* Determine the default page size of paginated list views
|
|
|
|
*/
|
|
|
|
public pageSize = [50, 100, 150, 200, 250];
|
|
|
|
|
2018-09-11 16:38:23 +02:00
|
|
|
/**
|
|
|
|
* The table itself
|
|
|
|
*/
|
|
|
|
@ViewChild(MatTable)
|
|
|
|
protected table: MatTable<V>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Table paginator
|
|
|
|
*/
|
|
|
|
@ViewChild(MatPaginator)
|
|
|
|
protected paginator: MatPaginator;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sorter for a table
|
|
|
|
*/
|
|
|
|
@ViewChild(MatSort)
|
|
|
|
protected sort: MatSort;
|
|
|
|
|
2019-01-29 12:11:11 +01:00
|
|
|
/**
|
|
|
|
* @returns the amount of currently dispalyed items (only showing items that pass all filters)
|
|
|
|
*/
|
|
|
|
public get filteredCount(): number {
|
|
|
|
return this.dataSource.filteredData.length;
|
|
|
|
}
|
|
|
|
|
2018-09-11 16:38:23 +02:00
|
|
|
/**
|
|
|
|
* Constructor for list view bases
|
|
|
|
* @param titleService the title serivce
|
|
|
|
* @param translate the translate service
|
2019-02-26 15:30:16 +01:00
|
|
|
* @param matSnackBar showing errors
|
|
|
|
* @param filterService filter
|
|
|
|
* @param sortService sorting
|
2018-09-11 16:38:23 +02:00
|
|
|
*/
|
2019-02-18 13:31:15 +01:00
|
|
|
public constructor(
|
|
|
|
titleService: Title,
|
|
|
|
translate: TranslateService,
|
|
|
|
matSnackBar: MatSnackBar,
|
2019-03-25 12:56:16 +01:00
|
|
|
protected route?: ActivatedRoute,
|
|
|
|
protected storage?: StorageService,
|
2019-02-18 13:31:15 +01:00
|
|
|
public filterService?: BaseFilterListService<M, V>,
|
|
|
|
public sortService?: BaseSortListService<V>
|
|
|
|
) {
|
2018-11-02 11:41:03 +01:00
|
|
|
super(titleService, translate, matSnackBar);
|
2018-11-05 17:40:32 +01:00
|
|
|
this.selectedRows = [];
|
2019-03-21 16:16:19 +01:00
|
|
|
try {
|
2019-03-25 12:56:16 +01:00
|
|
|
this.paginationStorageKey = (<Type<any>>route.component).name;
|
2019-03-21 16:16:19 +01:00
|
|
|
} catch (e) {
|
2019-03-25 12:56:16 +01:00
|
|
|
this.paginationStorageKey = '';
|
2019-03-21 16:16:19 +01:00
|
|
|
}
|
2018-09-11 16:38:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Children need to call this in their init-function.
|
|
|
|
* Calling these three functions in the constructor of this class
|
|
|
|
* would be too early, resulting in non-paginated tables
|
|
|
|
*/
|
2019-03-25 12:56:16 +01:00
|
|
|
public initTable(): void {
|
2018-09-11 16:38:23 +02:00
|
|
|
this.dataSource = new MatTableDataSource();
|
|
|
|
this.dataSource.paginator = this.paginator;
|
2019-03-21 16:16:19 +01:00
|
|
|
// Set the initial page settings.
|
2019-02-19 20:21:31 +01:00
|
|
|
if (this.dataSource.paginator) {
|
2019-03-21 16:16:19 +01:00
|
|
|
this.initializePagination();
|
2019-02-19 20:21:31 +01:00
|
|
|
this.dataSource.paginator._intl.itemsPerPageLabel = this.translate.instant('items per page');
|
|
|
|
}
|
2019-02-18 13:31:15 +01:00
|
|
|
if (this.filterService) {
|
|
|
|
this.onFilter();
|
|
|
|
}
|
|
|
|
if (this.sortService) {
|
|
|
|
this.onSort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Standard filtering function. Sufficient for most list views but can be overwritten
|
|
|
|
*/
|
|
|
|
protected onFilter(): void {
|
2019-02-25 14:36:04 +01:00
|
|
|
if (this.sortService) {
|
|
|
|
this.subscriptions.push(
|
|
|
|
this.filterService.filter().subscribe(filteredData => (this.sortService.data = filteredData))
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
this.filterService.filter().subscribe(filteredData => (this.dataSource.data = filteredData));
|
|
|
|
}
|
2019-02-18 13:31:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-03-13 15:16:06 +01:00
|
|
|
* Standard sorting function. Sufficient for most list views but can be overwritten
|
2019-02-18 13:31:15 +01:00
|
|
|
*/
|
|
|
|
protected onSort(): void {
|
2019-02-22 17:17:29 +01:00
|
|
|
this.subscriptions.push(
|
|
|
|
this.sortService.sort().subscribe(sortedData => {
|
|
|
|
// the dataArray needs to be cleared (since angular 7)
|
|
|
|
// changes are not detected properly anymore
|
|
|
|
this.dataSource.data = [];
|
|
|
|
this.dataSource.data = sortedData;
|
|
|
|
this.checkSelection();
|
|
|
|
})
|
|
|
|
);
|
2018-10-22 16:44:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public onSortButton(itemProperty: string): void {
|
|
|
|
let newOrder: 'asc' | 'desc' = 'asc';
|
|
|
|
if (itemProperty === this.sort.active) {
|
|
|
|
newOrder = this.sort.direction === 'asc' ? 'desc' : 'asc';
|
|
|
|
}
|
|
|
|
const newSort = {
|
|
|
|
disableClear: true,
|
|
|
|
id: itemProperty,
|
|
|
|
start: newOrder
|
|
|
|
};
|
|
|
|
this.sort.sort(newSort);
|
|
|
|
}
|
|
|
|
|
2019-01-10 12:54:48 +01:00
|
|
|
public onFilterData(filteredDataSource: MatTableDataSource<V>): void {
|
2018-10-22 16:44:18 +02:00
|
|
|
this.dataSource = filteredDataSource;
|
|
|
|
this.dataSource.paginator = this.paginator;
|
|
|
|
}
|
|
|
|
|
2019-04-04 14:52:47 +02:00
|
|
|
/**
|
|
|
|
* Central search/filter function. Can be extended and overwritten by a filterPredicate.
|
|
|
|
* Functions for that are usually called 'setFulltextFilter'
|
|
|
|
*
|
|
|
|
* @param event the string to search for
|
|
|
|
*/
|
2018-10-22 16:44:18 +02:00
|
|
|
public searchFilter(event: string): void {
|
|
|
|
this.dataSource.filter = event;
|
2018-09-11 16:38:23 +02:00
|
|
|
}
|
2018-11-05 17:40:32 +01:00
|
|
|
|
2019-03-21 16:16:19 +01:00
|
|
|
/**
|
|
|
|
* Initialize the settings for the paginator in every list view.
|
|
|
|
*/
|
|
|
|
private async initializePagination(): Promise<void> {
|
|
|
|
// If the storage is not available - like in history mode - do nothing.
|
|
|
|
if (this.storage) {
|
2019-03-25 12:56:16 +01:00
|
|
|
this.paginationStorageObject = (await this.storage.get('Pagination')) || {};
|
2019-03-21 16:16:19 +01:00
|
|
|
// Set the number of items per page -- by default to 25.
|
2019-03-25 12:56:16 +01:00
|
|
|
this.paginator.pageSize = this.paginationStorageObject[this.paginationStorageKey] || 25;
|
2019-03-21 16:16:19 +01:00
|
|
|
// Subscription to page change events, like size, index.
|
|
|
|
this.subscriptions.push(
|
2019-03-25 12:56:16 +01:00
|
|
|
this.paginator.page.subscribe((event: PageEvent) => {
|
|
|
|
this.setPageSettings(event.pageSize);
|
2019-03-21 16:16:19 +01:00
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to set the new selected page size in the browser's local storage.
|
|
|
|
*
|
|
|
|
* @param size is the new page size.
|
|
|
|
*/
|
|
|
|
public async setPageSettings(size: number): Promise<void> {
|
2019-03-25 12:56:16 +01:00
|
|
|
if (this.paginationStorageObject) {
|
|
|
|
this.paginationStorageObject[this.paginationStorageKey] = size;
|
|
|
|
await this.storage.set('Pagination', this.paginationStorageObject);
|
|
|
|
}
|
2019-03-21 16:16:19 +01:00
|
|
|
}
|
|
|
|
|
2018-11-05 17:40:32 +01:00
|
|
|
/**
|
|
|
|
* Default click action on selecting an item. In multiselect modus,
|
|
|
|
* this just adds/removes from a selection, else it performs a {@link singleSelectAction}
|
|
|
|
* @param row The clicked row's {@link ViewModel}
|
|
|
|
* @param event The Mouse event
|
|
|
|
*/
|
|
|
|
public selectItem(row: V, event: MouseEvent): void {
|
2019-02-26 11:06:42 +01:00
|
|
|
if (this.isMultiSelect) {
|
|
|
|
event.stopPropagation();
|
2018-11-05 17:40:32 +01:00
|
|
|
const idx = this.selectedRows.indexOf(row);
|
2018-12-10 17:54:48 +01:00
|
|
|
if (idx < 0) {
|
2018-11-05 17:40:32 +01:00
|
|
|
this.selectedRows.push(row);
|
|
|
|
} else {
|
|
|
|
this.selectedRows.splice(idx, 1);
|
|
|
|
}
|
2019-02-26 11:06:42 +01:00
|
|
|
} else {
|
|
|
|
event.stopPropagation();
|
|
|
|
this.singleSelectAction(row);
|
2018-11-05 17:40:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-20 12:45:34 +01:00
|
|
|
/**
|
|
|
|
* row clicks that should be ignored.
|
|
|
|
* Required for buttons or check boxes in tables
|
|
|
|
*
|
|
|
|
* @param event click event
|
|
|
|
*/
|
|
|
|
public ignoreClick(event: MouseEvent): void {
|
|
|
|
if (!this.isMultiSelect) {
|
|
|
|
event.stopPropagation();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-05 17:40:32 +01:00
|
|
|
/**
|
|
|
|
* Method to perform an action on click on a row, if not in MultiSelect Modus.
|
|
|
|
* Should be overridden by implementations. Currently there is no default action.
|
|
|
|
* @param row a ViewModel
|
|
|
|
*/
|
2018-12-10 17:54:48 +01:00
|
|
|
public singleSelectAction(row: V): void {}
|
2018-11-05 17:40:32 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* enables/disables the multiSelect Mode
|
|
|
|
*/
|
2018-12-10 17:54:48 +01:00
|
|
|
public toggleMultiSelect(): void {
|
2018-11-05 17:40:32 +01:00
|
|
|
if (!this.canMultiSelect || this.isMultiSelect) {
|
2018-12-12 18:06:06 +01:00
|
|
|
this._multiSelectMode = false;
|
2018-11-05 17:40:32 +01:00
|
|
|
this.clearSelection();
|
|
|
|
} else {
|
2018-12-12 18:06:06 +01:00
|
|
|
this._multiSelectMode = true;
|
2018-11-05 17:40:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-10 17:54:48 +01:00
|
|
|
/**
|
|
|
|
* Select all files in the current data source
|
|
|
|
*/
|
|
|
|
public selectAll(): void {
|
2019-01-29 12:11:11 +01:00
|
|
|
this.selectedRows = this.dataSource.filteredData;
|
2018-12-10 17:54:48 +01:00
|
|
|
}
|
|
|
|
|
2018-12-12 16:14:21 +01:00
|
|
|
public deselectAll(): void {
|
|
|
|
this.selectedRows = [];
|
|
|
|
}
|
|
|
|
|
2018-11-05 17:40:32 +01:00
|
|
|
/**
|
|
|
|
* Returns the current state of the multiSelect modus
|
|
|
|
*/
|
|
|
|
public get isMultiSelect(): boolean {
|
2018-12-12 18:06:06 +01:00
|
|
|
return this._multiSelectMode;
|
2018-11-05 17:40:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* checks if a row is currently selected in the multiSelect modus.
|
|
|
|
* @param item The row's entry
|
|
|
|
*/
|
|
|
|
public isSelected(item: V): boolean {
|
2018-12-12 18:06:06 +01:00
|
|
|
if (!this._multiSelectMode) {
|
2018-11-05 17:40:32 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return this.selectedRows.indexOf(item) >= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler to quickly unselect all items.
|
|
|
|
*/
|
|
|
|
public clearSelection(): void {
|
|
|
|
this.selectedRows = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks the array of selected items against the datastore data. This is
|
|
|
|
* meant to reselect items by their id even if some of their data changed,
|
|
|
|
* and to remove selected data that don't exist anymore.
|
|
|
|
* To be called after an update of data. Checks if updated selected items
|
|
|
|
* are still present in the dataSource, and (re-)selects them. This should
|
|
|
|
* be called as the observed datasource updates.
|
|
|
|
*/
|
|
|
|
protected checkSelection(): void {
|
|
|
|
const newSelection = [];
|
|
|
|
this.selectedRows.forEach(selectedrow => {
|
2019-01-29 12:11:11 +01:00
|
|
|
const newrow = this.dataSource.filteredData.find(item => item.id === selectedrow.id);
|
2018-11-05 17:40:32 +01:00
|
|
|
if (newrow) {
|
|
|
|
newSelection.push(newrow);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
this.selectedRows = newSelection;
|
|
|
|
}
|
2018-09-11 16:38:23 +02:00
|
|
|
}
|