Как лимитировать отображаемое количество строк в ячейке таблице в Angular ngx-bootstrap?
Есть компонент описывающий таблицу:
@Component({
selector: 'cmx-table',
templateUrl: './cmx-table.component.html',
styleUrls: ['./cmx-table.component.scss']
})
export class CmxTableComponent<T> implements OnInit, AfterViewInit {
public readonly tableSpinnerName = Constants.TABLE_SPINNER_NAME;
@Input() loadOnInit = true;
@Input() loadOnPageLimitChange = true;
@Input() useRouteForSettingsStore = true;
@Input() isLoading = false;
@Input() selectionType = 'checkbox';
@Input() rowClass: string;
@Input() countTitle = 'Всего записей';
@Input() countTitleStringTemplate = null;
@Input() showLastPage = true;
@Input() showPageControls = true;
@Input() useScrollPaging = false; // Not implemented
@Input() clientLimitPage = false;
@Input() groupExpansionDefault = false;
@Input() public hiddenColumns: Array<TableColumn> = [];
@Input() name = 'table'; // Name of table (need change if have more tables in page)
@Input() isPersistedSorts = true;
@Input() treeFromRelation: string;
@Input() treeToRelation: string;
@Input() withoutPaging = false;
@Input() pageLimitOptions = [{ value: 25 }, { value: 50 }, { value: 100 }, { value: 500 }];
private _tableModelInput = new BehaviorSubject<TableModel<T>>(undefined);
@Input() set tableModelInput(value) {
// set the latest value for _data BehaviorSubject
if (value !== undefined) {
this._tableModelInput.next(value);
}
}
// tslint:disable-next-line:no-input-rename
@Input('btnToolbarTemplate') public customBtnToolbarTemplate;
// tslint:disable-next-line:no-input-rename
@Input('groupHeader') public customGroupHeader;
@Input() displayCheck: (row: any, column?: any, value?: any) => boolean;
@Input() selectCheck: any;
@Output() readonly fetchData = new EventEmitter<TableModel<T>>();
@Output() readonly endFetchData = new EventEmitter<TableModel<T>>();
@Output() readonly selectRow = new EventEmitter<any>();
@Output() readonly changeHiddenColumns = new EventEmitter<any>();
@Output() readonly treeAction = new EventEmitter<any>();
@ViewChild('emptyTemplate', { static: true }) public emptyTemplate: TemplateRef<any>;
@ViewChild('idAnchorEditTemplate', { static: true }) public idAnchorEditTemplate: TemplateRef<any>;
@ViewChild('dateTemplate', { static: true }) public dateTemplate: TemplateRef<any>;
@ViewChild('dateTimeTemplate', { static: true }) public dateTimeTemplate: TemplateRef<any>;
@ViewChild('tooltipTemplate', { static: true }) public tooltipTemplate: TemplateRef<any>;
@ViewChild('indexTemplate', { static: true }) public indexTemplate: TemplateRef<any>;
@ViewChild('centerTemplate', { static: true }) public centerTemplate: TemplateRef<any>;
@ViewChild('booleanTemplate', { static: true }) public booleanTemplate: TemplateRef<any>;
@ViewChild('symbolicBooleanTemplate', { static: true }) public symbolicBooleanTempate: TemplateRef<any>;
@ViewChild('headLeftTemplate', { static: true }) public headLeftTemplate: TemplateRef<any>;
@ViewChild('codeTemplate', { static: true }) public codeTemplate: TemplateRef<any>;
@ViewChild('htmlTemplate', { static: true }) public htmlTemplate: TemplateRef<any>;
@ViewChild(DatatableComponent, { static: true }) table: DatatableComponent;
@ViewChild('btnToolbarTemplate', { static: true }) public defaultBtnToolbarTemplate;
@ViewChild('groupHeader', { static: true }) public defaultGroupHeader;
@ViewChild('tableSpinner', { static: true }) public tableSpinner: CmxSpinnerComponent;
public columns: Array<TableColumn> = [];
public messages = {
emptyMessage: 'В таблице нет данных.',
totalMessage: 'записей'
};
public tableModel: TableModel<T>;
get tableModelInput() {
// get the latest value from _data BehaviorSubject
return this._tableModelInput.getValue();
}
private pageLimitStoreName = 'table_limit';
private pageSortStoreName = 'table_sort';
private isSortLoadFromCookie = false; // Disable loading sorting from cookie
public currentPageLimit: number;
private readonly pageMaxSize = 2000;
public readonly pageControlTemplate = '$[]';
public get btnToolbarTemplate() {
return this.customBtnToolbarTemplate || this.defaultBtnToolbarTemplate;
}
public get groupHeader() {
return this.customGroupHeader || this.defaultGroupHeader;
}
constructor(
private cd: ChangeDetectorRef,
private logger: NGXLogger,
private cmxTableService: CmxTableService,
private storageService: StorageService,
private router: Router,
private notificationService: NotificationsService
) {}
ngOnInit(): void {
this.tableModel = new TableModel<T>();
this.cmxTableService.getColumnSubject().subscribe(e => {
this.logger.info('Subscribe, Table change columns');
});
const loadedSort = this.storageService.getSetting(this.pageSortStoreName, this.useRouteForSettingsStore);
this._tableModelInput.subscribe(
tableModel => {
this.logger.debug(`CmxTableComponent loaded model:`, tableModel);
this.tableModel = tableModel;
this.hideSpinner();
if (loadedSort && this.isSortLoadFromCookie) {
this.tableModel.Sort = loadedSort;
} else if (this.tableModel && this.sorts) {
this.tableModel.Sort = this.sorts;
}
this.endFetchData.next(this.tableModel);
this.changeLimit();
},
err => {
this.logger.debug('cmx-table', 'CmxTableComponent loaded model error');
this.hideSpinner();
}
);
}
ngAfterViewInit() {
setTimeout(() => {
this.loadStoredSettings();
if (this.useScrollPaging) {
this.showPageControls = false;
}
if (this.loadOnInit && UtilService.hasOnlyUndefinedProps(this.tableModelInput.FilterModel)) {
this.loadPage();
}
if (!this.withoutPaging) {
// Crutch for table limit set
const pageSize = this.storageService.getSetting(this.pageLimitStoreName, this.useRouteForSettingsStore, this.pageLimitOptions[1].value);
this.tableModel.PageSize = this.currentPageLimit = Number(pageSize);
} else {
this.tableModel.PageSize = this.currentPageLimit = this.pageMaxSize;
this.showPageControls = false;
}
});
}
public onResizeColumn($event) {
// Refresh column width in model
this.logger.debug('cmx-table', 'onResizeColumn', $event);
if (!$event.column) {
return;
}
this.columns.forEach((col, i) => {
let res1 = 0;
if (col.$$id === $event.column.$$id) {
res1 = col.width - $event.newValue;
col.width = $event.newValue;
}
const col2 = this.columns[i + 1];
if (col2) {
col2.width = col2.width + res1;
}
});
}
/**
* Loading page
*/
public loadPage(pageEvent = { offset: 0 }, filter: any = null) {
if (!this.tableModel.IsFilterValid) {
this.logger.debug('CmxTableComponent try to load page, the tableModel is not valid', pageEvent);
return;
}
this.logger.debug('CmxTableComponent load page, pageEvent: ', pageEvent);
this.tableModel.CurrentPageNumber = pageEvent.offset;
if (this.withoutPaging) {
this.tableModel.PageSize = this.pageMaxSize;
}
this.fetchData.emit(this.tableModel);
this.table.selected = [];
this.selectRow.emit(this.table.selected);
this.showSpinner();
}
public onChangePage($event) {
this.logger.debug('CmxTableComponent', 'onChangePage', $event);
if (!this.clientLimitPage) {
this.loadPage($event);
} else {
this.tableModel.CurrentPageNumber = $event.offset;
}
}
/**
* Change page soring
*/
public onSort(event) {
this.logger.debug('CmxTableComponent', 'onSort', this.sortType, event);
const hasChanges = this.tableModel.Sort.length !== event.sorts.length || this.tableModel.Sort.some((sort, index) => event.sorts[index].prop !== sort.prop);
if (hasChanges) {
this.tableModel.CurrentPageNumber = 0;
}
if (this.sortType === 'single') {
this.sorts = this.tableModel.Sort = [{ prop: event.column.prop, dir: event.newValue }];
} else {
this.sorts = this.tableModel.Sort = event.sorts.map(sortBy => {
return { prop: sortBy.prop, dir: sortBy.dir };
});
}
if (this.isPersistedSorts) {
this.storageService.setSetting(this.pageSortStoreName, this.tableModel.Sort, this.useRouteForSettingsStore);
}
this.loadPage();
}
...
}
Есть компонент, который её загружает:
@Component({
selector: 'app-suv-log',
templateUrl: './suv-log.component.html',
styleUrls: ['./suv-log.component.scss']
})
export class SuvLogComponent extends AbstractPageComponent<EventSuvLogModel> implements OnInit {
@ViewChild('eventCodeTemplate', { static: true }) public eventCodeTemplate: TemplateRef<any>;
...
@ViewChild('numberStrEventTemplate', { static: true }) public numberStrEventTemplate: TemplateRef<any>;
@Input() levelFilterOnly = false;
@Input() name = 'suv-log';
@Input() btnToolbarTemplate;
public listNumberStrEvent: Array<string> = ['5', '10', '20', '50', 'все'];
public ngSelectNumberStrEvent: Array<string> = ['10'];
constructor(
private service: MessageEventSuvLogService,
private clipboardService: ClipboardService,
private injector: Injector
) {
super(service, injector);
this.modalComponent = SuvLogModalComponent;
}
ngOnInit() {
this.table.columns = [
{
prop: 'id',
headerTemplate: this.table.emptyTemplate,
cellTemplate: this.table.emptyTemplate,
checkboxable: true,
canAutoResize: false,
headerCheckboxable: true,
width: 25,
sortable: false
},
{
name: 'Действия',
cellTemplate: this.xmlTemplate,
width: 80,
headerTemplate: this.table.emptyTemplate,
prop: 'xml',
canAutoResize: false,
sortable: false,
frozenLeft: false
},
{
name: 'Описание',
prop: 'description',
sortable: false,
width: 900,
cellTemplate: this.descrTemplate
}
];
this.hiddenColumns = [
{
name: 'Инициатор',
prop: 'source'
},
...
];
}
public getDateTimeFilter(dateTime, from, to) {
return UtilService.getDateParams(dateTime, from, to);
}
public onShowFull(row) {
const initData: ModalInitData = UtilService.cloneObject(this.initData);
initData.mode = new ModalMode();
initData.title = 'Просмотр';
initData.mode.toShowMode();
initData.form = row;
this.modalRef = this._bsModalService.show(SuvLogModalShowComponent, {
initialState: initData,
class: 'modal-xl'
});
}
...
public getRowColor(row) {
if (row.eventCode && row.eventCode.substr(4, 3) === 'WRN') {
return 'bg-light-warning';
} else if (row.eventCode && row.eventCode.substr(4, 3) === 'TCH') {
return 'bg-light-danger';
}
}
public setCountStrEvent(val) {
const numberStr: number = val ? Number(val[0]) : 10;
}
}
В поле
name:'Описание',
prop: 'description'
с сервера может прийти очень много строк, поэтому нужно дать возможность пользователю ограничивать отображаемое количество строк.
Загрузка идет в следующий html-шаблон:
<div class="row">
<div class="col">
<cmx-table
[rowClass]="eventCodeColoringEnabled ? getRowColor : ''"
(fetchData)="fetchData($event)"
[showLastPage]="false"
[hiddenColumns]="hiddenColumns"
[sorts]="[{ prop: 'dateTime', dir: 'desc' }]"
[loadOnInit]="false"
[loadOnPageLimitChange]="hasCompletedSearch"
[tableModelInput]="tableModel"
[name]="name"
[btnToolbarTemplate]="btnToolbarTemplate"
[useRouteForSettingsStore]="false"
selectionType="multiClick"
(selectRow)="onSelectRow($event)"
>
</cmx-table>
</div>
</div>
<ng-template #btnToolbarTemplate>
<ng-select
#numberStrEventTemplate
class="btn btn-primary"
[(ngModel)]="ngSelectNumberStrEvent[0]"
[items]="listNumberStrEvent"
[multiple]="false"
placeholder="Строк события"
(change)="setCountStrEvent(numberStrEventTemplate.selectedValues)"
></ng-select>
</ng-template>
В шаблоне предусмотрел для выбора количества строк в ячейке таблицы <ng-select>...</ng-select>
В компоненте обработчик изменения setCountStrEvent(val)
И ng-select и обработчик отрабатывают.
Как на фронте менять в шаблоне таблицы отображаемое количество строк в ячейках столбца 'description'?