<cds-table-container [cdsLayer]="layer"
- [cdsTheme]="theme">
+ [cdsTheme]="theme"
+ class="content-theme">
<cds-table-toolbar #toolbar
*ngIf="toolHeader"
(cancel)="onBatchActionsCancel()"
</div>
</div>
<!-- end filter chips for column filters -->
- <cds-table [model]="model"
- [sortable]="!!userConfig.sorts"
- [size]="size"
- class="overflow-y-hidden"
- [skeleton]="false"
- [showSelectionColumn]="selectionType === 'multiClick'"
- [enableSingleSelect]="selectionType === 'single'"
- [stickyHeader]="false"
- [striped]="false"
- [isDataGrid]="false"
- (sort)="changeSorting($event)"
- (selectRow)="onSelect($event)"
- (selectAll)="onSelectAll($event)"
- (deselectRow)="onDeselect($event)"
- (deselectAll)="onDeselectAll($event)">
- <tbody>
- <tr cdstablerow
- *ngIf="!rows?.length && !loadingIndicator">
- <td class="no-data"
- cdstabledata
- [attr.colspan]="selectionType === 'single' ? visibleColumns.length + 1 : visibleColumns.length + 2">
- <span class="d-flex justify-content-center align-items-center"
- i18n>No data to display</span>
- </td>
- </tr>
- <tr cdstablerow
- *ngIf="loadingIndicator">
- <td class="no-data"
- cdstabledata
- [attr.colspan]="visibleColumns.length + 1">
- <span class="d-flex justify-content-center align-items-center"
- i18n>Loading</span>
- </td>
- </tr>
+ <table cdsTable
+ [sortable]="sortable"
+ [noBorder]="false"
+ [size]="size"
+ [striped]="false"
+ [skeleton]="loadingIndicator">
+ <thead cdsTableHead
+ [sortable]="sortable"
+ (deselectAll)="onDeselectAll()"
+ (selectAll)="onSelectAll()"
+ (sort)="changeSorting($event)"
+ [model]="model"
+ [showSelectionColumn]="showSelectionColumn"
+ [enableSingleSelect]="enableSingleSelect"
+ [skeleton]="loadingIndicator"
+ [stickyHeader]="false">
+ </thead>
+ <tbody cdsTableBody
+ *ngIf="!noData; else noDataTemplate"
+ [skeleton]="loadingIndicator">
+ <ng-container *ngFor="let row of model.data; let i = index; trackBy: trackByFn.bind(this, identifier)">
+ <tr cdsTableRow
+ [model]="model"
+ [row]="row"
+ [size]="size"
+ [selected]="model.isRowSelected(i)"
+ [expandable]="model.isRowExpandable(i)"
+ [expanded]="model.isRowExpanded(i)"
+ [showSelectionColumn]="showSelectionColumn"
+ [enableSingleSelect]="enableSingleSelect"
+ [skeleton]="loadingIndicator"
+ (selectRow)="onSelect(i)"
+ (deselectRow)="onDeselect(i)"
+ (expandRow)="model.expandRow(i, !model.isRowExpanded(i))"
+ (rowClick)="onSelect(i)"
+ *ngIf="!model.isRowFiltered(i)">
+ </tr>
+ <tr cdsTableExpandedRow
+ cdsExpandedRowHover
+ *ngIf="model.isRowExpandable(i) && !shouldExpandAsTable(row) && !model.isRowFiltered(i)"
+ [row]="row"
+ [expanded]="model.isRowExpanded(i)"
+ [skeleton]="loadingIndicator">
+ </tr>
+ <ng-container *ngIf="model.isRowExpandable(i) && shouldExpandAsTable(row) && model.isRowExpanded(i) && !model.isRowFiltered(i)">
+ <tr cdsTableRow
+ *ngFor="let expandedDataRow of firstExpandedDataInRow(row)"
+ [model]="model"
+ [showSelectionColumnCheckbox]="false"
+ [showSelectionColumn]="showSelectionColumn"
+ [row]="expandedDataRow"
+ [size]="size"
+ [selected]="model.isRowSelected(i)"
+ [skeleton]="loadingIndicator">
+ </tr>
+ </ng-container>
+ </ng-container>
</tbody>
- </cds-table>
+ </table>
<cds-pagination [model]="model"
(selectPage)="onPageChange($event)"
[disabled]="limit === 0"
+ [skeleton]="loadingIndicator"
[pageInputDisabled]="limit === 0">
</cds-pagination>
</cds-table-container>
+<ng-template #noDataTemplate>
+ <tbody>
+ <tr cdstablerow>
+ <td *ngIf="!rows?.length && !loadingIndicator"
+ class="no-data"
+ cdstabledata
+ [attr.colspan]="visibleColumns.length + 2">
+ <span class="d-flex justify-content-center align-items-center"
+ i18n>No data to display</span>
+ </td>
+ </tr>
+ </tbody>
+</ng-template>
+
<ng-template #rowDetailTpl
let-row="data">
<div *ngIf="row[identifier] === expanded?.[identifier]"
import { CdTableColumnFilter } from '~/app/shared/models/cd-table-column-filter';
import { CdTableColumnFiltersChange } from '~/app/shared/models/cd-table-column-filters-change';
import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context';
-import { PageInfo } from '~/app/shared/models/cd-table-paging';
import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
import { CdUserConfig } from '~/app/shared/models/cd-user-config';
import { TimerService } from '~/app/shared/services/timer.service';
import { TableActionsComponent } from '../table-actions/table-actions.component';
import { TableDetailDirective } from '../directives/table-detail.directive';
-import { filter, map, throttleTime } from 'rxjs/operators';
+import { filter, map } from 'rxjs/operators';
import { CdSortDirection } from '../../enum/cd-sort-direction';
import { CdSortPropDir } from '../../models/cd-sort-prop-dir';
private _expanded: any = undefined;
+ get sortable() {
+ return !!this.userConfig?.sorts;
+ }
+
+ get noData() {
+ return !this.rows?.length && !this.loadingIndicator;
+ }
+
+ get showSelectionColumn() {
+ return this.selectionType === 'multiClick';
+ }
+
+ get enableSingleSelect() {
+ return this.selectionType === 'single';
+ }
+
+ /**
+ * Controls if all checkboxes are viewed as selected.
+ */
+ selectAllCheckbox = false;
+
+ /**
+ * Controls the indeterminate state of the header checkbox.
+ */
+ selectAllCheckboxSomeSelected = false;
+
/**
* To prevent making changes to the original columns list, that might change
* how the table is renderer a second time, we now clone that list into a
size: this.model.pageLength,
filteredData: value
});
- this.model.totalDataLength = value?.length || 0;
+ this.model.totalDataLength = this.serverSide ? this.count : value?.length || 0;
}
get rows() {
return search.split(' ').filter((word) => word);
}
- shouldThrottle(): number {
- if (this.autoReload === -1) {
- return 500;
- }
- return 0;
- }
-
ngAfterViewInit(): void {
if (this.tableActions?.dropDownActions?.length) {
this.tableColumns = [
return false;
}
return true;
- }),
- throttleTime(this.shouldThrottle(), undefined, {
- leading: true,
- trailing: false
})
)
.subscribe({
let tableItem = new TableItem({
selected: val,
data: {
- value: column.pipe ? column.pipe.transform(rowValue || val) : rowValue,
+ value: column.pipe ? column.pipe.transform(rowValue) : rowValue,
row: val,
column: { ...column, ...val }
}
tableItem.data = { ...tableItem.data, row: val };
if (this.hasDetails) {
- (tableItem.expandedData = val), (tableItem.expandedTemplate = this.rowDetailTpl);
+ tableItem.expandedData = val;
+ tableItem.expandedTemplate = this.rowDetailTpl;
}
}
}
});
+ const rowsChangeSubscription = this.model.rowsSelectedChange.subscribe(() =>
+ this.updateSelectAllCheckbox()
+ );
+ const dataChangeSubscription = this.model.dataChange.subscribe(() => {
+ this.updateSelectAllCheckbox();
+ });
+
this._subscriptions.add(tableHeadersSubscription);
this._subscriptions.add(datasetSubscription);
this._subscriptions.add(rowsExpandedSubscription);
+ this._subscriptions.add(rowsChangeSubscription);
+ this._subscriptions.add(dataChangeSubscription);
}
ngOnInit() {
this.userConfig.limit = this.limit;
}
if (!(this.userConfig.offset >= 0)) {
- // this.userConfig.offset = this.model.currentPage;
+ this.userConfig.offset = this.model.currentPage - 1;
}
if (!this.userConfig.search) {
this.userConfig.search = this.search;
ngOnChanges(changes: SimpleChanges) {
if (changes?.data?.currentValue) {
- if (_.isNil(this.expanded)) {
- this.useData();
- } else if (this.model.rowsExpanded.every((x) => !x)) {
- this.expanded = undefined;
- }
+ this.useData();
}
}
this.reloadData();
}
- changePage(pageInfo: PageInfo) {
- this.userConfig.offset = pageInfo.offset;
- this.userConfig.limit = pageInfo.limit;
+ onPageChange(page: number) {
+ this.model.currentPage = page;
+
+ this.userConfig.offset = this.model.currentPage - 1;
+ this.userConfig.limit = this.model.pageLength;
+
if (this.serverSide) {
this.reloadData();
+ return;
}
- }
- onPageChange(page: number) {
- this.model.currentPage = page;
this.doPagination({});
}
size = this.model.pageLength,
filteredData = this.rows
}): void {
+ if (this.serverSide) {
+ this._dataset.next(filteredData);
+ return;
+ }
+
if (this.limit === 0) {
this.model.currentPage = 1;
this.model.pageLength = filteredData.length;
this.updateColumnFilterOptions();
this.updateFilter();
this.reset();
+ this.doSorting();
this.updateSelected();
this.updateExpanded();
this.toggleExpandRow();
- this.doSorting();
}
/**
}
}
- onSelect($event: any) {
- const { selectedRowIndex } = $event;
+ onSelect(selectedRowIndex: number) {
const selectedData = _.get(this.model.data?.[selectedRowIndex], [0, 'selected']);
+ this.model.selectRow(selectedRowIndex, true);
if (this.selectionType === 'single') {
this.selection.selected = [selectedData];
} else {
this.updateSelection.emit(this.selection);
}
- onSelectAll($event: TableModel) {
- $event.rowsSelected.forEach((isSelected: boolean, rowIndex: number) =>
+ onSelectAll() {
+ this.model.selectAll(!this.selectAllCheckbox && !this.selectAllCheckboxSomeSelected);
+ this.model.rowsSelected.forEach((isSelected: boolean, rowIndex: number) =>
this._toggleSelection(rowIndex, isSelected)
);
this.updateSelection.emit(this.selection);
+ this.cdRef.detectChanges();
}
- onDeselect($event: any) {
+ onDeselect(deselectedRowIndex: number) {
+ this.model.selectRow(deselectedRowIndex, false);
if (this.selectionType === 'single') {
return;
}
- const { deselectedRowIndex } = $event;
this._toggleSelection(deselectedRowIndex, false);
this.updateSelection.emit(this.selection);
}
- onDeselectAll($event: TableModel) {
- $event.rowsSelected.forEach((isSelected: boolean, rowIndex: number) =>
+ onDeselectAll() {
+ this.model.selectAll(false);
+ this.model.rowsSelected.forEach((isSelected: boolean, rowIndex: number) =>
this._toggleSelection(rowIndex, isSelected)
);
this.updateSelection.emit(this.selection);
(_, rowIndex: number) => rowIndex === expandedRowIndex
);
}
+
+ firstExpandedDataInRow(row: TableItem[]) {
+ const found = row.find((d) => d.expandedData);
+ if (found) {
+ return found.expandedData;
+ }
+ return found;
+ }
+
+ shouldExpandAsTable(row: TableItem[]) {
+ return row.some((d) => d.expandAsTable);
+ }
+
+ isRowExpandable(index: number) {
+ return this.model.data[index].some((d) => d && d.expandedData);
+ }
+
+ trackByFn(id: string, _index: number, row: TableItem[]) {
+ const uniqueIdentifier = _.get(row, [0, 'data', 'row', id])?.toString?.();
+ return uniqueIdentifier || row;
+ }
+
+ updateSelectAllCheckbox() {
+ const selectedRowsCount = this.model.selectedRowsCount();
+
+ if (selectedRowsCount <= 0) {
+ // reset select all checkbox if nothing selected
+ this.selectAllCheckbox = false;
+ this.selectAllCheckboxSomeSelected = false;
+ } else if (selectedRowsCount < this.model.data.length) {
+ this.selectAllCheckbox = true;
+ this.selectAllCheckboxSomeSelected = true;
+ } else {
+ this.selectAllCheckbox = true;
+ this.selectAllCheckboxSomeSelected = false;
+ }
+ }
}