OpenSlides/client/src/app/site/base/list-view-base.ts

254 lines
7.8 KiB
TypeScript
Raw Normal View History

2018-10-22 16:44:18 +02:00
import { MatTableDataSource, MatTable, MatSort, MatPaginator, MatSnackBar } from '@angular/material';
import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
2018-10-22 16:44:18 +02:00
import { ViewChild } from '@angular/core';
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';
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';
export abstract class ListViewBaseComponent<V extends BaseViewModel, M extends BaseModel> extends BaseViewComponent {
/**
* The data source for a table. Requires to be initialized with a BaseViewModel
*/
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;
/**
* Current state of the multi select mode. TODO Could be merged with edit mode?
2018-11-05 17:40:32 +01:00
*/
private _multiSelectMode = false;
2018-11-05 17:40:32 +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[];
/**
* 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;
/**
* @returns the amount of currently dispalyed items (only showing items that pass all filters)
*/
public get filteredCount(): number {
return this.dataSource.filteredData.length;
}
/**
* Constructor for list view bases
* @param titleService the title serivce
* @param translate the translate service
* @param matSnackBar showing errors
* @param filterService filter
* @param sortService sorting
*/
public constructor(
titleService: Title,
translate: TranslateService,
matSnackBar: MatSnackBar,
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 = [];
}
/**
* 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
*/
public initTable(): void {
this.dataSource = new MatTableDataSource();
this.dataSource.paginator = this.paginator;
2019-02-19 20:21:31 +01:00
if (this.dataSource.paginator) {
this.dataSource.paginator._intl.itemsPerPageLabel = this.translate.instant('items per page');
}
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 {
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));
}
}
/**
* Standard sorting function. Sufficient for most list views but can be overwritten
*/
protected onSort(): void {
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;
}
public searchFilter(event: string): void {
this.dataSource.filter = event;
}
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
}
}
/**
* 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) {
this._multiSelectMode = false;
2018-11-05 17:40:32 +01:00
this.clearSelection();
} else {
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 {
this.selectedRows = this.dataSource.filteredData;
2018-12-10 17:54:48 +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 {
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 {
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 => {
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;
}
}