@universis/ngx-tables 0.6.18 → 0.6.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -540,6 +540,9 @@ class AdvancedTableComponent {
540
540
  this.init = new EventEmitter();
541
541
  this.reload = new EventEmitter();
542
542
  this.select = new EventEmitter();
543
+ this.draw = new EventEmitter();
544
+ this.page = new EventEmitter();
545
+ this.scroll = new EventEmitter();
543
546
  this.selectedItems = new EventEmitter();
544
547
  this.scrollable = true;
545
548
  this.serverSide = true;
@@ -667,32 +670,55 @@ class AdvancedTableComponent {
667
670
  // simple search
668
671
  search(searchText) {
669
672
  this.query = this.query || this._context.model(this.config.model).asQueryable();
670
- if (searchText && this.config.searchExpression) {
671
- // validate search text in double quotes
672
- if (/^"(.*?)"$/.test(searchText)) {
673
- this.query.setParam('$filter', this._template.transform(this.config.searchExpression, {
674
- text: searchText.replace(/^"|"$/g, ''),
675
- }));
673
+ if (this.serverSide) {
674
+ if (searchText && this.config.searchExpression) {
675
+ // validate search text in double quotes
676
+ if (/^"(.*?)"$/.test(searchText)) {
677
+ this.query.setParam('$filter', this._template.transform(this.config.searchExpression, {
678
+ text: searchText.replace(/^"|"$/g, ''),
679
+ }));
680
+ }
681
+ else {
682
+ // try to split words
683
+ const words = searchText.split(' ');
684
+ // join words to a single filter
685
+ const filter = words.map((word) => {
686
+ return this._template.transform(this.config.searchExpression, {
687
+ text: word,
688
+ });
689
+ }).join(' and ');
690
+ // set filter
691
+ this.query.setParam('$filter', filter);
692
+ }
676
693
  }
677
694
  else {
678
- // try to split words
679
- const words = searchText.split(' ');
680
- // join words to a single filter
681
- const filter = words.map((word) => {
682
- return this._template.transform(this.config.searchExpression, {
683
- text: word,
684
- });
685
- }).join(' and ');
686
- // set filter
687
- this.query.setParam('$filter', filter);
695
+ // clear filter
696
+ this.query.setParam('$filter', null);
688
697
  }
698
+ this._firstLoad = false;
699
+ this.dataTable.draw();
689
700
  }
690
701
  else {
691
- // clear filter
692
- this.query.setParam('$filter', null);
702
+ // ensure empty table layout
703
+ this.resetEmptyTable();
704
+ // search locally
705
+ this.dataTable.search(searchText.trim()).draw();
706
+ // get indexes of filtered rows
707
+ const indexes = Array.from(this.dataTable.context[0].aiDisplay);
708
+ // set recordsFiltered
709
+ const recordsFiltered = indexes.length;
710
+ // set recordsTotal
711
+ const recordsTotal = indexes.length;
712
+ // retrieve data from indexes
713
+ const rows = Array.from(this.dataTable.rows().data());
714
+ const data = indexes.map(index => rows[index]);
715
+ // emit results to load output
716
+ this.load.emit({
717
+ data: data,
718
+ recordsFiltered: recordsFiltered,
719
+ recordsTotal: recordsTotal
720
+ });
693
721
  }
694
- this._firstLoad = false;
695
- this.dataTable.draw();
696
722
  }
697
723
  fetch(clearSelected) {
698
724
  // if data table is null
@@ -1019,6 +1045,20 @@ class AdvancedTableComponent {
1019
1045
  }, 0);
1020
1046
  }
1021
1047
  });
1048
+ tableElement.on('draw.dt', (event) => {
1049
+ this.draw.emit();
1050
+ if (this.dataTable) {
1051
+ const info = this.dataTable.page.info();
1052
+ this.page.emit(info);
1053
+ try {
1054
+ this.dataTable.columns.adjust();
1055
+ }
1056
+ catch (err) {
1057
+ console.error('ERROR', 'An error occurred while adjusting table columns');
1058
+ console.error(err);
1059
+ }
1060
+ }
1061
+ });
1022
1062
  // allow selection
1023
1063
  if (this.selectable) {
1024
1064
  // get first column (as row primary key)
@@ -1043,10 +1083,6 @@ class AdvancedTableComponent {
1043
1083
  // user interface additions
1044
1084
  // handle page change event (a trick for firing draw.dt events)
1045
1085
  tableElement
1046
- // for each page
1047
- .on('page.dt', ($event) => {
1048
- const info = this.dataTable.page.info();
1049
- })
1050
1086
  // handle draw events
1051
1087
  .on('draw.dt', () => {
1052
1088
  if (this.customSelect) {
@@ -1147,11 +1183,11 @@ class AdvancedTableComponent {
1147
1183
  // get order expression
1148
1184
  const order = aoData[2].value;
1149
1185
  // get skip records param
1150
- const skip = aoData[3].value;
1186
+ let skip = aoData[3].value;
1151
1187
  // get page size param
1152
- const top = aoData[4].value;
1188
+ let top = aoData[4].value;
1153
1189
  // get search value
1154
- const searchText = aoData[5].value;
1190
+ const search = aoData[5].value;
1155
1191
  // get data queryable
1156
1192
  const q = self._query instanceof ClientDataQueryable ? self._query : self._context.model(self.config.model).asQueryable();
1157
1193
  // apply paging params
@@ -1278,15 +1314,22 @@ class AdvancedTableComponent {
1278
1314
  }
1279
1315
  if (this.selectable) ;
1280
1316
  // handle init.dt event and emit local event
1281
- this.dataTable = tableElement.DataTable(settings);
1282
- tableElement.on('init.dt', () => {
1283
- if (this.dataTable == null) {
1284
- return;
1285
- }
1286
- // restore default empty table html
1287
- // (due to ng async binding)
1288
- this.init.emit(this.dataTable);
1289
- });
1317
+ this.dataTable = tableElement.on('init.dt', (event) => {
1318
+ const scroller = jQuery(event.target).parent();
1319
+ // handle scrolling
1320
+ scroller.on('scroll', (scrollEvent) => {
1321
+ const target = jQuery(scrollEvent.target);
1322
+ const top = target.scrollTop();
1323
+ const left = target.scrollLeft();
1324
+ if (this.dataTable) {
1325
+ this.scroll.emit({
1326
+ top,
1327
+ left
1328
+ });
1329
+ }
1330
+ });
1331
+ this.init.emit();
1332
+ }).DataTable(settings);
1290
1333
  // show or hide header
1291
1334
  if (!this.showHeader) {
1292
1335
  this._element.nativeElement
@@ -1305,6 +1348,16 @@ class AdvancedTableComponent {
1305
1348
  }
1306
1349
  }
1307
1350
  }
1351
+ scrollTop(value) {
1352
+ if (this.dataTable) {
1353
+ this.dataTable.parent().scrollTop(value);
1354
+ }
1355
+ }
1356
+ scrollLeft(value) {
1357
+ if (this.dataTable) {
1358
+ this.dataTable.parent().scrollLeft(value);
1359
+ }
1360
+ }
1308
1361
  get lastQuery() {
1309
1362
  if (this.config == null) {
1310
1363
  return null;
@@ -1355,6 +1408,10 @@ class AdvancedTableComponent {
1355
1408
  const query = this._context.model(this.config.model).asQueryable();
1356
1409
  // select attributes
1357
1410
  query.setParam('$select', this.lastQueryParams.$select);
1411
+ if (this.lastQueryParams.$expand) {
1412
+ // apply expand attributes
1413
+ query.setParam('$expand', this.lastQueryParams.$expand);
1414
+ }
1358
1415
  // apply filter for row
1359
1416
  findKeys.forEach((key) => {
1360
1417
  const column = this.config.columns.find((col) => {
@@ -1441,7 +1498,8 @@ class AdvancedTableComponent {
1441
1498
  // due to ng data binding
1442
1499
  // empty table template has no translated text during ngOnInit()
1443
1500
  if (this.dataTable && this.dataTable.context[0]) {
1444
- this.dataTable.context[0].oLanguage.sEmptyTable = this.emptyTable.nativeElement.innerHTML;
1501
+ this.dataTable.context[0].oLanguage.sEmptyTable =
1502
+ this.dataTable.context[0].oLanguage.sZeroRecords = this.emptyTable.nativeElement.innerHTML;
1445
1503
  }
1446
1504
  }
1447
1505
  clearEmptyTable() {
@@ -1538,6 +1596,9 @@ AdvancedTableComponent.propDecorators = {
1538
1596
  init: [{ type: Output, args: ['init',] }],
1539
1597
  reload: [{ type: Input, args: ['reload',] }],
1540
1598
  select: [{ type: Output, args: ['select',] }],
1599
+ draw: [{ type: Output, args: ['draw',] }],
1600
+ page: [{ type: Output, args: ['page',] }],
1601
+ scroll: [{ type: Output, args: ['scroll',] }],
1541
1602
  selectedItems: [{ type: Output, args: ['selectedItems',] }],
1542
1603
  scrollable: [{ type: Input }],
1543
1604
  serverSide: [{ type: Input }],
@@ -1983,7 +2044,10 @@ class AdvancedSearchFormComponent extends AdvancedTableSearchBaseComponent {
1983
2044
  const value = this.formComponent.submission.data[key];
1984
2045
  // the filter with type is a string and is not empty
1985
2046
  const isEmptyString = typeof value === 'string' && value.length === 0;
1986
- if (!isEmptyString) {
2047
+ // do not include empty objects in filter
2048
+ const isEmptyObject = typeof value === 'object' && Object.keys(value).length === 0;
2049
+ // make isEmptyString, isEmptyObject mutex
2050
+ if (!isEmptyString && !isEmptyObject) {
1987
2051
  Object.defineProperty(this.filter, key, {
1988
2052
  configurable: true,
1989
2053
  enumerable: true,
@@ -3863,7 +3927,7 @@ class AdvancedAggregatorListComponent {
3863
3927
  AdvancedAggregatorListComponent.decorators = [
3864
3928
  { type: Component, args: [{
3865
3929
  selector: 'app-advanced-aggregator-list',
3866
- template: "<div id=\"group-list\" class=\"flex-column\" *ngIf=\"config && config.columns\">\n <ul class=\"sis--list-group sis--list-group-header\">\n <li class=\"sis--list-group-item text-wrap\">\n <div class=\"d-flex justify-content-center align-items-center\" *ngIf=\"body && body.length > 0\">\n <div *ngIf=\"config.actions && config.actions.length > 0\" class=\"sis--list-group-item_sm_column\"></div>\n <div *ngFor=\"let column of config.columns; let i = index\" class=\"text-left\"\n [class]=\"config.columns[i].length\"\n [translate]=\"config.columns[i].type === 'action' ? '' : column.title\" [ngClass]=\"{\n 'd-none': config.columns[i].hidden\n }\"></div>\n </div>\n </li>\n </ul>\n\n <!-- The list of entries -->\n <ul class=\"sis--list-group sis--list-group-body\" *ngIf=\"body && body.length > 0\">\n <li class=\"sis--list-group-item text-wrap\" *ngFor=\"let entry of body | orderBy: config.orderBy\">\n <div class=\"d-flex justify-content-center align-items-center list-color\">\n <!-- The list of actions -->\n <div *ngIf=\"config.actions && config.actions.length > 0\" class=\"sis--list-group-item_sm_column text-left pr-0\">\n <button *ngFor=\"let action of config.actions\" (click)=\"callCallback(action.identifier, entry)\"\n [title]=\"(action.title ? action.title : '') | translate \" class=\"btn btn-default px-0\">\n <i *ngIf=\"action.type === 'edit'\" class=\"fas fa-edit text-indigo\"></i>\n </button>\n <span class=\"px-0\">\n <i class=\"fa fa-fw\">&nbsp;</i>\n </span>\n\n </div>\n <!-- The list of data -->\n <div *ngFor=\"let column of config.columns; let i=index\" [class]=\"config.columns[i].length\" class=\"text-left\"\n [ngClass]=\"{\n 'text-truncate': config.columns[i].hasOwnProperty('truncate') && config.columns[i].truncate,\n 'd-none': config.columns[i].hidden,\n 'd-md-block': !config.columns[i].hidden\n }\"\n [title]=\"config.columns[i].hasOwnProperty('truncate') && config.columns[i].truncate ? entry[column.name] : ''\">\n <span class=\"text-secondary\"\n *ngIf=\"!config.columns[i].hasOwnProperty('type') || (config.columns[i].hasOwnProperty('type') && config.columns[i].type === 'text')\"\n [ngClass]=\"{\n 'font-xs': config.columns[i].variant !== 'primary',\n 'text-body': config.columns[i].variant === 'primary',\n 'text-success': config.columns[i].variant === 'redGreen' && config.columns[i].hasOwnProperty('condition') && entry[config.columns[i].condition],\n 'text-danger': config.columns[i].variant === 'redGreen' && config.columns[i].hasOwnProperty('condition') && !entry[config.columns[i].condition],\n 'font-lg': config.columns[i].variant === 'redGreen' && entry[column.name] && entry[column.name].length > 3,\n 'font-2xl': config.columns[i].variant === 'redGreen' && entry[column.name] && entry[column.name].length <= 3\n }\">\n {{entry[column.name] || '-'}}\n </span>\n\n <button *ngIf=\"config.columns[i].type === 'action' && config.columns[i].variant === 'edit'\"\n (click)=\"callCallback(config.columns[i].identifier, entry)\"\n [title]=\"(config.columns[i].title ? config.columns[i].title : '') | translate \"\n class=\"btn btn-default px-0 mx-0\">\n <i class=\"fas fa-edit text-indigo\"></i>\n </button>\n\n <span *ngIf=\"config.columns[i].type === 'boolean'\">\n <i class=\"fa font-lg\" [ngClass]=\"{\n 'fa-check text-success': entry[column.name].toString() === config.columns[i].trueValue,\n 'fa-times text-danger': entry[column.name].toString() !== config.columns[i].trueValue && config.columns[i].variant !== 'checkOrDash'\n }\"></i>\n <span\n *ngIf=\"entry[column.name].toString() !== config.columns[i].trueValue && config.columns[i].variant === 'checkOrDash'\"\n class=\"text-secondary font-xs\">-</span>\n </span>\n </div>\n </div>\n <div *ngIf=\"entry && entry.subitems\">\n <div *ngFor=\"let subitem of entry.subitems\" class=\"d-flex justify-content-center align-items-center list-color\">\n <div *ngIf=\"config.actions && config.actions.length > 0\" class=\"sis--list-group-item_sm_column text-left pr-0\">\n <span class=\"px-0\">\n <i class=\"fa fa-fw\">&nbsp;</i>\n </span>\n <button *ngFor=\"let action of config.actions\" (click)=\"callCallback(action.identifier, subitem)\"\n [title]=\"(action.title ? action.title : '') | translate \" class=\"btn btn-default px-0\">\n <i *ngIf=\"action.type === 'edit'\" class=\"fas fa-edit text-indigo\"></i>\n </button>\n </div>\n <div class=\"text-left\"></div>\n <div *ngFor=\"let column of config.columns; let i=index\" [class]=\"config.columns[i].length\" class=\"text-left\"\n [ngClass]=\"{\n 'text-truncate': config.columns[i].hasOwnProperty('truncate') && config.columns[i].truncate,\n 'd-none': config.columns[i].hidden,\n 'd-md-block': !config.columns[i].hidden\n }\"\n [title]=\"config.columns[i].hasOwnProperty('truncate') && config.columns[i].truncate ? entry[column.name] : ''\">\n <span class=\"text-secondary\"\n *ngIf=\"!config.columns[i].hasOwnProperty('type') || (config.columns[i].hasOwnProperty('type') && config.columns[i].type === 'text')\"\n [ngClass]=\"{\n 'font-xs': config.columns[i].variant !== 'primary',\n 'text-body': config.columns[i].variant === 'primary',\n 'text-success': config.columns[i].variant === 'redGreen' && config.columns[i].hasOwnProperty('condition') && subitem[config.columns[i].condition],\n 'text-danger': config.columns[i].variant === 'redGreen' && config.columns[i].hasOwnProperty('condition') && !subitem[config.columns[i].condition],\n 'font-lg': config.columns[i].variant === 'redGreen' && subitem[column.name] && subitem[column.name].length > 3,\n 'font-2xl': config.columns[i].variant === 'redGreen' && subitem[column.name] && subitem[column.name].length <= 3\n }\">\n {{subitem[column.name] || '-'}}\n </span>\n <span *ngIf=\"config.columns[i].type === 'boolean'\">\n <i class=\"fa font-lg\" [ngClass]=\"{\n 'fa-check text-success': subitem[column.name].toString() === config.columns[i].trueValue,\n 'fa-times text-danger': subitem[column.name].toString() !== config.columns[i].trueValue && config.columns[i].variant !== 'checkOrDash'\n }\"></i>\n <span\n *ngIf=\"subitem[column.name].toString() !== config.columns[i].trueValue && config.columns[i].variant === 'checkOrDash'\"\n class=\"text-secondary font-xs\">-</span>\n </span>\n </div>\n </div>\n </div>\n </li>\n </ul>\n</div>\n"
3930
+ template: "<div id=\"group-list\" class=\"flex-column\" *ngIf=\"config && config.columns\">\n <ul class=\"sis--list-group sis--list-group-header\">\n <li class=\"sis--list-group-item text-wrap\">\n <div class=\"d-flex justify-content-center align-items-center\" *ngIf=\"body && body.length > 0\">\n <div *ngIf=\"config.actions && config.actions.length > 0\" class=\"sis--list-group-item_sm_column\"></div>\n <div *ngFor=\"let column of config.columns; let i = index\" class=\"text-left\"\n [class]=\"config.columns[i].length\"\n [translate]=\"config.columns[i].type === 'action' ? '' : column.title\" [ngClass]=\"{\n 'd-none': config.columns[i].hidden\n }\"></div>\n </div>\n </li>\n </ul>\n\n <!-- The list of entries -->\n <ul class=\"sis--list-group sis--list-group-body\" *ngIf=\"body && body.length > 0\">\n <li class=\"sis--list-group-item text-wrap\" *ngFor=\"let entry of body | orderBy: config.orderBy\">\n <div class=\"d-flex justify-content-center align-items-center list-color\">\n <!-- The list of actions -->\n <div *ngIf=\"config.actions && config.actions.length > 0\" class=\"sis--list-group-item_sm_column text-left pr-0\">\n <button *ngFor=\"let action of config.actions\" (click)=\"callCallback(action.identifier, entry)\"\n [title]=\"(action.title ? action.title : '') | translate \" class=\"btn btn-default px-0\">\n <i class=\"\" [class]=\"'fas text-indigo ml-2 ' + 'fa-' + action.icon\"></i>\n </button>\n <span class=\"px-0\">\n <i class=\"fa fa-fw\">&nbsp;</i>\n </span>\n\n </div>\n <!-- The list of data -->\n <div *ngFor=\"let column of config.columns; let i=index\" [class]=\"config.columns[i].length\" class=\"text-left\"\n [ngClass]=\"{\n 'text-truncate': config.columns[i].hasOwnProperty('truncate') && config.columns[i].truncate,\n 'd-none': config.columns[i].hidden,\n 'd-md-block': !config.columns[i].hidden\n }\"\n [title]=\"config.columns[i].hasOwnProperty('truncate') && config.columns[i].truncate ? entry[column.name] : ''\">\n <span class=\"text-secondary\"\n *ngIf=\"!config.columns[i].hasOwnProperty('type') || (config.columns[i].hasOwnProperty('type') && config.columns[i].type === 'text')\"\n [ngClass]=\"{\n 'font-xs': config.columns[i].variant !== 'primary',\n 'text-body': config.columns[i].variant === 'primary',\n 'text-success': config.columns[i].variant === 'redGreen' && config.columns[i].hasOwnProperty('condition') && entry[config.columns[i].condition],\n 'text-danger': config.columns[i].variant === 'redGreen' && config.columns[i].hasOwnProperty('condition') && !entry[config.columns[i].condition],\n 'font-lg': config.columns[i].variant === 'redGreen' && entry[column.name] && entry[column.name].length > 3,\n 'font-2xl': config.columns[i].variant === 'redGreen' && entry[column.name] && entry[column.name].length <= 3\n }\">\n {{entry[column.name] || '-'}}\n </span>\n\n <span *ngIf=\"config.columns[i].type === 'boolean'\">\n <i class=\"fa font-lg\" [ngClass]=\"{\n 'fa-check text-success': entry[column.name].toString() === config.columns[i].trueValue,\n 'fa-times text-danger': entry[column.name].toString() !== config.columns[i].trueValue && config.columns[i].variant !== 'checkOrDash'\n }\"></i>\n <span\n *ngIf=\"entry[column.name].toString() !== config.columns[i].trueValue && config.columns[i].variant === 'checkOrDash'\"\n class=\"text-secondary font-xs\">-</span>\n </span>\n </div>\n </div>\n <div *ngIf=\"entry && entry.subitems\">\n <div *ngFor=\"let subitem of entry.subitems\" class=\"d-flex justify-content-center align-items-center list-color\">\n <div *ngIf=\"config.actions && config.actions.length > 0\" class=\"sis--list-group-item_sm_column text-left pr-0\">\n <span class=\"px-0\">\n <i class=\"fa fa-fw\">&nbsp;</i>\n </span>\n <button *ngFor=\"let action of config.actions\" (click)=\"callCallback(action.identifier, subitem)\"\n [title]=\"(action.title ? action.title : '') | translate \" class=\"btn btn-default px-0\">\n <i *ngIf=\"action.type === 'edit'\" class=\"fas fa-edit text-indigo\"></i>\n </button>\n </div>\n <div class=\"text-left\"></div>\n <div *ngFor=\"let column of config.columns; let i=index\" [class]=\"config.columns[i].length\" class=\"text-left\"\n [ngClass]=\"{\n 'text-truncate': config.columns[i].hasOwnProperty('truncate') && config.columns[i].truncate,\n 'd-none': config.columns[i].hidden,\n 'd-md-block': !config.columns[i].hidden\n }\"\n [title]=\"config.columns[i].hasOwnProperty('truncate') && config.columns[i].truncate ? entry[column.name] : ''\">\n <span class=\"text-secondary\"\n *ngIf=\"!config.columns[i].hasOwnProperty('type') || (config.columns[i].hasOwnProperty('type') && config.columns[i].type === 'text')\"\n [ngClass]=\"{\n 'font-xs': config.columns[i].variant !== 'primary',\n 'text-body': config.columns[i].variant === 'primary',\n 'text-success': config.columns[i].variant === 'redGreen' && config.columns[i].hasOwnProperty('condition') && subitem[config.columns[i].condition],\n 'text-danger': config.columns[i].variant === 'redGreen' && config.columns[i].hasOwnProperty('condition') && !subitem[config.columns[i].condition],\n 'font-lg': config.columns[i].variant === 'redGreen' && subitem[column.name] && subitem[column.name].length > 3,\n 'font-2xl': config.columns[i].variant === 'redGreen' && subitem[column.name] && subitem[column.name].length <= 3\n }\">\n {{subitem[column.name] || '-'}}\n </span>\n <span *ngIf=\"config.columns[i].type === 'boolean'\">\n <i class=\"fa font-lg\" [ngClass]=\"{\n 'fa-check text-success': subitem[column.name].toString() === config.columns[i].trueValue,\n 'fa-times text-danger': subitem[column.name].toString() !== config.columns[i].trueValue && config.columns[i].variant !== 'checkOrDash'\n }\"></i>\n <span\n *ngIf=\"subitem[column.name].toString() !== config.columns[i].trueValue && config.columns[i].variant === 'checkOrDash'\"\n class=\"text-secondary font-xs\">-</span>\n </span>\n </div>\n </div>\n </div>\n </li>\n </ul>\n</div>\n"
3867
3931
  }] }
3868
3932
  ];
3869
3933
  /** @nocollapse */