UNPKG

flx-ui-datatable

Version:

## AUTHOR Felix Kakra Acheampong from (`Orcons Systems`) Ghana

732 lines (721 loc) 151 kB
/** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ import { Component, Input, Output, EventEmitter } from '@angular/core'; import { Validators, FormBuilder } from '@angular/forms'; import { FlxUiDatatableService } from './flx-ui-datatable.service'; import { BehaviorSubject } from 'rxjs'; export class FlxUiDatatableComponent { /** * @param {?} __form * @param {?} service */ constructor(__form, service) { this.__form = __form; this.service = service; this.classes = {}; this.headers = []; this.lazyloadingConfig = {}; this.embedPictures = {}; this.dataKeys = []; this.enableDataExports = false; this.dataExportsConfig = {}; this.searchKeys = []; this.dataSrcKey = ''; this.hasActionButtons = false; this.hideNumbers = false; this.enableMultipleSelection = false; this.multipleSelectKey = ''; this.hasAddButton = false; this.dataUrl = ''; this.actionButtonStart = false; this.multipleSelectButton = { text: 'Selected', icon: '' }; this.searchPlaceholder = 'Enter name to search'; this.actionHeader = 'Actions'; this.limit = 20; this.spinnerSrc = ''; this.actionButtons = []; this.paginationButtons = { background: '#dddddd', textColor: '#335599' }; this.tableHeader = { background: '#ffffff', textColor: '#335599' }; this.searchButton = { background: '#cccccc', textColor: '#335599' }; this.addButton = {}; this.searchBar = { borderSize: '1px', borderColor: '#ccc', background: '#ffffff', textColor: '#000000' }; this.actionButtonsConfig = { position: 'right', beforeNumbers: true }; this.firstActionButtonClicked = new EventEmitter(); this.secondActionButtonClicked = new EventEmitter(); this.thirdActionButtonClicked = new EventEmitter(); this.multipleSelectClicked = new EventEmitter(); this.addButtonClicked = new EventEmitter(); /** * 18/12/2018 */ this.textSubstring = {}; this.conditionManipulator = {}; this.isExportAll = false; this.tData = []; this.behavior = new BehaviorSubject([]); this.searchDataTempOffset = []; this.displayData = this.behavior.asObservable(); this.offset = 1; this.searchResultFound = []; } /** * @return {?} */ reload() { this.service.loadFlxDataTableData(this.reloadUrl, true); } /** * * @param {?} checked Export all selection * @return {?} */ checkToExportOption(checked) { this.isExportAll = checked; } /** * * @param {?} exportType Export type: print|pdf|excel|word * @return {?} */ exportDocumentsAs(exportType) { let /** @type {?} */ loading = /** @type {?} */ (document.getElementById("export_loading")); loading.style.display = 'block'; let /** @type {?} */ headers = (!this.dataExportsConfig.dataColumns || this.dataExportsConfig.dataColumns.length < 1) ? this.dataKeys : this.dataExportsConfig.dataColumns; let /** @type {?} */ dataToExport = (!this.isExportAll) ? this.displayData : this.service.flxData; //Subscribe to data dataToExport.subscribe((data) => { let /** @type {?} */ arrayObj = []; //Loop and push data for (let /** @type {?} */ d of data) { let /** @type {?} */ obj = {}; for (let /** @type {?} */ h = 0; h < headers.length; h++) { obj[headers[h]] = d[headers[h]]; } arrayObj.push(obj); } if (exportType == 'print') { try { printJS({ printable: arrayObj, properties: headers, type: 'json' }); loading.style.display = 'none'; } catch (/** @type {?} */ e) { loading.style.display = 'none'; // console.log('PrintJS not found. Add `https://printjs-4de6.kxcdn.com/print.min.js` to your index.html or add as part of angular.json script') ; } } else { let /** @type {?} */ extension = (exportType == 'pdf') ? 'pdf' : (exportType == 'excel') ? 'xlsx' : 'docx'; let /** @type {?} */ pageId = (exportType == 'pdf') ? 3 : (exportType == 'excel') ? 5 : 4; let /** @type {?} */ requestData = { "data": JSON.stringify(arrayObj) }; this.service.postData('http://exporter.azurewebsites.net/api/export/ExportFromJSON/', pageId, requestData).subscribe((resp) => { var /** @type {?} */ download = 'http://exporter.azurewebsites.net/api/export/GetFile/' + resp; download += "?fileName=andrei&extension=" + extension; window.location.href = download; loading.style.display = 'none'; }, (e => { //console.log('file export error',e) ; })); } }).unsubscribe(); } /** * @return {?} */ hasImageEmbeded() { return this.embedPictures.hasOwnProperty("index"); } /** * @return {?} */ getImage() { console.log('eoeoe'); // let img = new Image() ; // img.src = imageSrc ; // img.onload = ((e)=>{ // return imageSrc ; // }) ; // img.onerror = ((e)=>{ // return this.embedPictures.fallbackUrl ; // }) } /** * @param {?} JSONData * @param {?} ReportTitle * @param {?} ShowLabel * @return {?} */ JSONToCSVConvertor(JSONData, ReportTitle, ShowLabel) { //If JSONData is not an object then JSON.parse will parse the JSON string in an Object var /** @type {?} */ arrData = typeof JSONData != 'object' ? JSON.parse(JSONData) : JSONData; var /** @type {?} */ CSV = ''; //Set Report title in first row or line CSV += ReportTitle + '\r\n\n'; //This condition will generate the Label/Header if (ShowLabel) { var /** @type {?} */ row = ""; //This loop will extract the label from 1st index of on array for (var /** @type {?} */ index in arrData[0]) { //Now convert each value to string and comma-seprated row += index + ','; } row = row.slice(0, -1); //append Label row with line break CSV += row + '\r\n'; } //1st loop is to extract each row for (var /** @type {?} */ i = 0; i < arrData.length; i++) { var /** @type {?} */ row = ""; //2nd loop will extract each column and convert it in string comma-seprated for (var /** @type {?} */ index in arrData[i]) { row += '"' + arrData[i][index] + '",'; } row.slice(0, row.length - 1); //add a line break after each row CSV += row + '\r\n'; } if (CSV == '') { alert("Invalid data"); return; } //Generate a file name var /** @type {?} */ fileName = "MyReport_"; //this will remove the blank-spaces from the title and replace it with an underscore fileName += ReportTitle.replace(/ /g, "_"); //Initialize file format you want csv or xls var /** @type {?} */ uri = 'data:text/csv;charset=utf-8,' + escape(CSV); // Now the little tricky part. // you can use either>> window.open(uri); // but this will not work in some browsers // or you will not get the correct file extension //this trick will generate a temp <a /> tag var /** @type {?} */ link = document.createElement("a"); link.href = uri; //set the visibility hidden so it will not effect on your web-layout link.style = "visibility:hidden"; link.download = fileName + ".csv"; //this part will append the anchor tag and remove it after automatic click document.body.appendChild(link); link.click(); document.body.removeChild(link); } /** * * @param {?} newData * @return {?} */ changeDisplayData(newData) { this.behavior.next(newData); this.setColumnWidths(); } /** * @return {?} */ ngOnInit() { $(window).resize(function () { /* Same as before */ }).resize(); if (this.isLazyloadingEnabled()) { this.reloadUrl = this.dataUrl + '&' + this.lazyloadingConfig.apiOffsetKey + '=0&' + this.lazyloadingConfig.apiSearchKey + '='; } else { this.reloadUrl = this.dataUrl; } this.searchForm = this.__form.group({ searchString: ['', Validators.required] }); this.searchForm = this.__form.group({ searchString: ['', Validators.required] }); this.service.limit = this.limit; this.service.setLazyloadingConfig(this.lazyloadingConfig); this.service.setDataUrl(this.dataUrl); this.service.setDataSrcKey(this.dataSrcKey); let /** @type {?} */ url = (this.isLazyloadingEnabled()) ? this.dataUrl + '&' + this.lazyloadingConfig.apiOffsetKey + '=0&' + this.lazyloadingConfig.apiSearchKey + '=' : this.dataUrl; this.service.loadFlxDataTableData(url); this.service.flxData.subscribe((resp) => { this.tData = resp; let /** @type {?} */ obj = []; if (this.tData.length > this.limit) { for (let /** @type {?} */ i = 0; i < this.limit; i++) { obj.push(this.tData[i]); } // this.service.dataOffset = this.limit; } else { let /** @type {?} */ counter = 0; for (let /** @type {?} */ i = 0; i < this.tData.length; i++) { obj.push(this.tData[i]); counter++; } // this.service.dataOffset = obj.length; } this.searchDataTempOffset = obj; this.changeDisplayData(obj); }); } /** * @param {?} values * @param {?} form * @return {?} */ searchDataInApi(values, form) { this.service.chageDataTable([]); this.service.loadFlxDataTableData(this.dataUrl + '&' + this.lazyloadingConfig.apiOffsetKey + '=0&' + this.lazyloadingConfig.apiSearchKey + '=' + values.searchString); } /** * @return {?} */ ngAfterViewInit() { let /** @type {?} */ eachColumnWidth = this.returColumnswidth(); $(window).ready(function () { $(".flx-table-thead tr th").css({ 'width': eachColumnWidth + '%' }); }); } /** * @return {?} */ setColumnWidths() { let /** @type {?} */ eachColumnWidth = this.returColumnswidth(); $(window).ready(function () { $(".flx-table-data tbody tr td").css({ 'width': eachColumnWidth + '%' }); }); } /** * @return {?} */ returColumnswidth() { let /** @type {?} */ hasNumbers = (!this.hideNumbers) ? 0 : 1; let /** @type {?} */ hasActionButtons = (!this.hasActionButtons) ? 0 : 1; let /** @type {?} */ numberOfColumns = (this.headers.length + 1) + hasActionButtons + hasNumbers; console.log('number_of_columns', numberOfColumns); let /** @type {?} */ eachColumnWidth = 100 / numberOfColumns; return eachColumnWidth; } /** * @param {?} index * @param {?} buttonIndex * @return {?} */ actionButtonClicked(index, buttonIndex) { this.displayData.subscribe((data) => { if (buttonIndex == 0) { return this.firstActionButtonClicked.emit({ index: index, data: data[index] }); } else if (buttonIndex == 1) { return this.secondActionButtonClicked.emit({ index: index, data: data[index] }); } else { this.thirdActionButtonClicked.emit({ index: index, data: data[index] }); } }).unsubscribe(); } /** * @return {?} */ addButtonClick() { this.addButtonClicked.emit(); } /** * @return {?} */ confirmDelete() { return this.multipleSelectClicked.emit(this.service.multipleDeletion); } /** * @param {?} checked * @return {?} */ addRemove(checked) { if (checked) { this.displayData.subscribe((data) => { let /** @type {?} */ counter = 0; for (let /** @type {?} */ i of data) { try { this.service.multipleDeletion.push(i[this.multipleSelectKey]); } catch (/** @type {?} */ e) { } } // console.log(this.service.multipleDeletion) ; }).unsubscribe(); } else { this.service.multipleDeletion = []; } } /** * @param {?} dataKeyvalue * @param {?} index * @param {?} selected * @return {?} */ addRemoveDeleteItem(dataKeyvalue, index, selected) { if (!selected) { for (var /** @type {?} */ i = 0; i < this.service.multipleDeletion.length; i++) { if (dataKeyvalue == this.service.multipleDeletion[i]) { this.service.multipleDeletion.splice(i, 1); break; } } } else { this.displayData.subscribe((resp) => { this.service.multipleDeletion.push(resp[index][this.multipleSelectKey]); }).unsubscribe(); } // console.log('left '+dataKeyvalue,this.service.multipleDeletion) ; } /** * @return {?} */ getSearchColumns() { return (this.hasAddButton) ? (this.enableDataExports) ? 'col-md-6 search-container' : 'col-md-7 search-container' : (this.enableDataExports) ? 'col-md-7 search-container' : 'col-md-8 search-container'; } /** * @return {?} */ disablePrevtButton() { return Math.ceil(this.service.dataOffset / this.limit) <= 1; } /** * @return {?} */ disableNextButton() { return Math.ceil(this.service.dataOffset / this.limit) == Math.ceil(this.service.totalItems / this.limit); } /** * @return {?} */ isLazyloadingEnabled() { return this.lazyloadingConfig.hasOwnProperty("apiOffsetKey") && this.lazyloadingConfig.apiOffsetKey; } /** * @param {?} type * @return {?} */ nextPrevItem(type) { if (this.isLazyloadingEnabled()) { this.service.loadFinish = false; this.service.getDataLength().then(dataLength => { this.service.chageDataTable([]); this.service.dataOffset = (type == 'prev') ? ((this.service.dataOffset - this.limit) - this.limit) : this.service.dataOffset; let /** @type {?} */ url = (this.isLazyloadingEnabled()) ? this.dataUrl + '&' + this.lazyloadingConfig.apiOffsetKey + '=' + this.service.dataOffset + '&' + this.lazyloadingConfig.apiSearchKey + '=' : this.dataUrl; this.service.loadFlxDataTableData(url); }).catch(e => { //console.log('error',e) ; }); return; } // Paginate if lazyloading is disabled if (type == 'prev') { this.paginateDatatableRecord((this.service.dataOffset - this.limit) - this.limit); } else { if (this.service.dataOffset < this.limit) { this.paginateDatatableRecord(this.service.dataOffset + (this.limit - 1)); } else { this.paginateDatatableRecord(this.service.dataOffset); } } } /** * @param {?=} searchString * @return {?} */ filterData(searchString = '') { if (searchString == '') { this.isSearching = false; } else { this.isSearching = true; } this.changeDisplayData([]); this.service.flxData.subscribe((data) => { let /** @type {?} */ searchResults = []; //If no string provided. Register all the previous data to the dataset if (searchString.trim() == '') { this.changeDisplayData(this.searchDataTempOffset); return; } //Check if searchKeys are set else use dataKeys as searchKeys let /** @type {?} */ searchKeys = (this.searchKeys.length < 1) ? this.dataKeys : this.searchKeys; for (let /** @type {?} */ i = 0; i < data.length; i++) { //Variable to check if data is found let /** @type {?} */ found = -1; for (let /** @type {?} */ x = 0; x < searchKeys.length; x++) { try { if (data[i][String(searchKeys[x])].toLowerCase().indexOf(searchString.toLocaleLowerCase()) !== -1) { found = i; break; } } catch (/** @type {?} */ e) { } } //If found push the index of the data to the searchResults variable if (found > -1) { searchResults.push(data[found]); } } this.searchResultFound = searchResults; //Register the results to the dataset this.changeDisplayData(searchResults); this.setColumnWidths(); }).unsubscribe(); } /** * @param {?} value offset value * @return {?} */ paginateDatatable(value) { // Check if lazy loading is enabled if (this.isLazyloadingEnabled()) { this.service.loadFinish = false; // Subscribe to get the data length this.service.getDataLength().then(() => { this.service.chageDataTable([]); // Check if all is selected to prevent NAN value if (value != 'all') { this.service.dataOffset = parseInt(value); } // setup url let /** @type {?} */ url = (this.isLazyloadingEnabled()) ? this.dataUrl + '&' + this.lazyloadingConfig['apiOffsetKey'] + '=' + value + '&' + this.lazyloadingConfig.apiSearchKey + '=' : this.dataUrl; // paginate this.service.loadFlxDataTableData(url, false); }).catch(e => { // console.log('error',e) ; }); return; } this.paginateDatatableRecord(value); } /** * * @param {?} value pagination number * Perform pagination to the dataset * @return {?} */ paginateDatatableRecord(value) { if (this.lazyloadingConfig.hasOwnProperty("apiOffsetKey") && this.lazyloadingConfig['apiOffsetKey']) { this.service.loadFinish = false; this.service.getDataLength().then(dataLength => { this.service.chageDataTable([]); this.service.dataOffset = parseInt(value) + this.limit; this.service.loadFlxDataTableData(this.dataUrl + '&' + this.lazyloadingConfig.apiOffsetKey + '=' + value + '&' + this.lazyloadingConfig.apiSearchKey + '='); this.setColumnWidths(); }).catch(e => { // console.log('error',e) ; }); return; } let /** @type {?} */ num = parseInt(value); if (num <= 0) { this.offset = 1; this.service.dataOffset = this.limit; } else { if (value != 'all') { this.offset = num + 1; this.service.dataOffset = num + this.limit; } else { this.offset = 1; } } this.service.flxData.subscribe((data) => { if (value !== 'all') { let /** @type {?} */ paginateResult = []; for (let /** @type {?} */ i = value; i < (this.limit + parseInt(value)); i++) { if (data[i]) { paginateResult.push(data[i]); } } if (paginateResult.length > 0) { this.changeDisplayData(paginateResult); } } else { this.changeDisplayData(data); this.searchDataTempOffset = data; } this.setColumnWidths(); }).unsubscribe(); } } FlxUiDatatableComponent.decorators = [ { type: Component, args: [{ selector: 'flx-ui-datatable', template: `<div class="col-md-12 flx-ui-datatable-main {{ classes?.maincontainer }}"> <div id="export_loading" class="col-md-12 text-center" style="display: none;margin-bottom:0.5em;color:#dddddd;font-size:13px;font-weight:bold;">Exporting...</div> <div class="col-md-12 flx-ui-datatable-header"> <div class="container-fluid"> <div class="row"> <div class="col-xs-3 col-sm-3 pagination-select col-md-2" style="position:relative;z-index: 1;"> <select class="form-control rmsh rmrd {{ classes?.paginationselect }}" (change)="paginateDatatable($event?.target?.value)"> <option *ngFor="let paging of service?.pagination" [value]="paging?.value">{{ paging?.label }}</option> </select> </div> <div class="col-xs-5 col-sm-5 col-md-2 text-center flx-datatable-pagination rmpd" style="position:relative;z-index: 2;"> <button mat-icon-button [ngClass]="{'flx-pagination-end': disablePrevtButton()}" (click)="nextPrevItem('prev')" [disabled]="disablePrevtButton()" class="flx-ui-datatable-pagination-buttons {{ classes?.paginationButton }}"><span class="fa fa-angle-double-left fa-1x"></span> <span class="flx-datatable-tooltip-prev" [ngClass]="{'flx-pagination-end': disablePrevtButton()}">Previous</span> </button> <span *ngIf="!isSearching;else searching">{{ service?.dataOffset | ceil: limit }} / {{ service?.totalItems | ceil: limit }}</span> <ng-template #searching> </ng-template> <button mat-icon-button [ngClass]="{'flx-pagination-end': disableNextButton()}" (click)="nextPrevItem('next')" [disabled]="disableNextButton()" class="flx-ui-datatable-pagination-buttons {{ classes?.paginationButton }}"><span class="fa fa-angle-double-right fa-1x"></span> <span class="flx-datatable-tooltip-next" [ngClass]="{'flx-pagination-end': disableNextButton()}">Next</span></button> </div> <div [class]="'search-bar '+getSearchColumns()"> <input type="text" *ngIf="!isLazyloadingEnabled()" [style.background]="searchBar?.background" [style.color]="searchBar?.textColor" [ngStyle]="{border:searchBar?.borderSize +' solid '+ searchBar?.borderColor} " (keyup)="filterData($event?.target?.value)" class="form-control rmsh rmrd customclass" [placeholder]="searchPlaceholder"> <form (ngSubmit)="searchDataInApi(srch?.value,srch)" #srch="ngForm" *ngIf="isLazyloadingEnabled()"> <div class="input-group"> <input type="text" required name="searchString" ngModel [style.background]="searchBar?.background" [style.color]="searchBar?.textColor" [ngStyle]="{border:searchBar?.borderSize +' solid '+ searchBar?.borderColor} " class="form-control rmsh rmrd {{ classes?.searchbar }}" [placeholder]="searchPlaceholder"> <span class="input-group-addon"> <button [disabled]="!srch?.valid" type="submit" class="btn btn-default btn-clear btn-md"> <i class="fa fa-search"></i> </button> </span> </div> </form> </div> <div class="col-md-1 text-right rmpd" *ngIf="hasAddButton"> <button (click)="addButtonClick()" class="{{ classes?.addButton }}" [style.background]="addButton?.background" [style.borderColor]="addButton?.background" [style.color]="addButton?.textColor"><span class="fa fa-plus"></span> Add</button> </div> <div class="col-md-1 text-right rmpd export-cnt" *ngIf="enableDataExports"> <div class="dropdown"> <button class="btn btn-default {{ classes?.exportButton }} dropdown-toggle" type="button" data-toggle="dropdown"> <i class="fa fa-angle-down"></i> </button> <ul class="dropdown-menu dropdown-menu-export {{ classes?.exportDropdown }}"> <li class="dropdown-header">{{ dataExportsConfig?.title }}. <input type="checkbox" (change)="checkToExportOption($event?.target?.checked)" style="position: relative;top:0.3em;"> <sup style="font-size:10px;color:#999;"> All</sup></li> <li class="divider"></li> <li class="dropdown-submenu" *ngFor="let export of dataExportsConfig?.exportsTo" (click)="exportDocumentsAs(export)"> <a href="javascript:void(0)" class="btn btn-default btn-sm form-control" *ngIf="export=='print'"><i class="glyphicon glyphicon-print"></i> Print</a> <a href="javascript:void(0)" class="btn btn-default btn-sm form-control" *ngIf="export=='pdf'" style="color:#ff0000"><i class="glyphicon glyphicon-file"></i> PDF</a> <a href="javascript:void(0)" class="btn btn-default btn-sm form-control" *ngIf="export=='excel'" style="color:#009900;"><i class="glyphicon glyphicon-file"></i> Excel</a> <a href="javascript:void(0)" class="btn btn-default btn-sm form-control" *ngIf="export=='word'" style="color:#335599;"><i class="glyphicon glyphicon-file"></i> Word</a> </li> <li class="divider"></li> <li class="dropdown-header"> <span *ngIf="!isExportAll">{{ (displayData | async)?.length }}</span> <span *ngIf="isExportAll">{{ (service?.flxData | async)?.length }}</span> </li> </ul> </div> </div> </div> </div> </div> <div class="container-fluid"> <div class="row"> <div class="col-md-12 rmpd table-responsive flx-tabl-container"> <table class="table {{ classes?.tableType }} th-table"> <thead class="{{ classes?.tableHeader }} flx-table-thead"> <tr> <!-- Actions buttons header left before numbers --> <th *ngIf="hasActionButtons && (actionButtonsConfig?.position=='left' && actionButtonsConfig?.beforeNumbers)">{{ actionHeader }} <input type="checkbox" [checked]="service?.multipleDeletion?.length>0" (change)="addRemove($event?.target?.checked)" *ngIf="enableMultipleSelection"> <button class="btn btn-danger btn-xs flx-multiple-deletion-button" *ngIf="enableMultipleSelection && service?.multipleDeletion?.length>0" (click)="confirmDelete()"><span [class]="multipleSelectButton?.icon"></span> {{ multipleSelectButton?.text }}</button> </th> <th *ngIf="!hideNumbers">N<sup><u>o</u></sup></th> <!-- Actions buttons header left after numbers --> <th *ngIf="hasActionButtons && (actionButtonsConfig?.position=='left' && !actionButtonsConfig?.beforeNumbers)">{{ actionHeader }} <input type="checkbox" [checked]="service?.multipleDeletion?.length>0" (change)="addRemove($event?.target?.checked)" *ngIf="enableMultipleSelection"> <button class="btn btn-danger btn-xs flx-multiple-deletion-button" *ngIf="enableMultipleSelection && service?.multipleDeletion?.length>0" (click)="confirmDelete()"><span [class]="multipleSelectButton?.icon"></span> {{ multipleSelectButton?.text }}</button> </th> <th *ngFor="let header of headers">{{ header }}</th> <!-- Actions buttons header right --> <th *ngIf="hasActionButtons && (actionButtonsConfig?.position=='right')">{{ actionHeader }} <input type="checkbox" [checked]="service?.multipleDeletion?.length>0" (change)="addRemove($event?.target?.checked)" *ngIf="enableMultipleSelection"> <button class="btn btn-danger btn-xs flx-multiple-deletion-button" *ngIf="enableMultipleSelection && service?.multipleDeletion?.length>0" (click)="confirmDelete()"><span [class]="multipleSelectButton?.icon"></span> {{ multipleSelectButton?.text }}</button> </th> </tr> </thead> </table> <perfect-scrollbar class="data-scroll-content {{ classes?.dataScrollContent }}"> <table class="table {{ classes?.tableType }} flx-table-data"> <tbody class="flxdatatablebodyscrollbar"> <tr *ngIf="!service?.loadFinish"> <td colspan="20" class="text-center"> <img *ngIf="spinnerSrc" [class]="classes?.spinner" [src]="spinnerSrc" style="margin-top:1em;margin-bottom:1em;"> </td> </tr> <tr class="flxuidatatablerow" id="flxdatatable_singlerow" *ngFor="let data of displayData | async;let i=index"> <!-- Buttons Left Before Numbers --> <td class="table-buttons" *ngIf="hasActionButtons && (actionButtonsConfig?.position=='left' && actionButtonsConfig?.beforeNumbers)" scope="row"> <span *ngFor="let aButton of actionButtons;let buttonIndex=index"> <button type="button" (click)="actionButtonClicked(i,buttonIndex)" class="btn {{ aButton?.class }}"> <div class="toltip" class="flx-tooltip" [ngClass]="{'flx-tooltip-left':aButton?.tooltipPosition=='left','flx-tooltip-bottom':aButton?.tooltipPosition=='bottom','flx-tooltip-right':aButton?.tooltipPosition=='right'}" *ngIf="aButton?.tooltip">{{ aButton?.tooltip }}</div> <span class="action-button-icon-left" [class]="aButton?.icon" *ngIf="!aButton?.iconPosition || aButton?.iconPosition!='right'"></span> <span class="button-text"> {{ aButton?.text }} </span> <span [class]="aButton?.icon" *ngIf="aButton?.iconPosition=='right'"></span> </button> </span> <input type="checkbox" checked (change)="addRemoveDeleteItem(data[multipleSelectKey],i,$event?.target?.checked)" *ngIf="enableMultipleSelection && service?.multipleDeletion?.length>0"> </td> <!-- Numbers --> <td class="{{ classes?.tableData }}" *ngIf="!hideNumbers" style="color: #999;">{{ offset+i }}</td> <!-- Buttons Left After Numbers --> <td class="table-buttons" *ngIf="hasActionButtons && (actionButtonsConfig?.position=='left' && !actionButtonsConfig?.beforeNumbers)" scope="row"> <span *ngFor="let aButton of actionButtons;let buttonIndex=index"> <button type="button" (click)="actionButtonClicked(i,buttonIndex)" class="btn {{ aButton?.class }}"> <div class="toltip" class="flx-tooltip" [ngClass]="{'flx-tooltip-left':aButton?.tooltipPosition=='left','flx-tooltip-bottom':aButton?.tooltipPosition=='bottom','flx-tooltip-right':aButton?.tooltipPosition=='right'}" *ngIf="aButton?.tooltip">{{ aButton?.tooltip }}</div> <span class="action-button-icon-left" [class]="aButton?.icon" *ngIf="!aButton?.iconPosition || aButton?.iconPosition!='right'"></span> <span class="button-text"> {{ aButton?.text }} </span> <span [class]="aButton?.icon" *ngIf="aButton?.iconPosition=='right'"></span> </button> </span> <input type="checkbox" checked (change)="addRemoveDeleteItem(data[multipleSelectKey],i,$event?.target?.checked)" *ngIf="enableMultipleSelection && service?.multipleDeletion?.length>0"> </td> <!-- Main --> <td class="{{ classes?.tableData }}" *ngFor="let dataKey of dataKeys;let x=index"> <img *ngIf="hasImageEmbeded() && x==embedPictures?.index; else nopicture" [class]="'img-fall-back ' +embedPictures?.class" [src]="embedPictures?.rootFolder+data[dataKey]" [flx-ui-datatable-img-fallback]="embedPictures?.fallbackUrl" > <ng-template #nopicture> <section *ngIf="textSubstring?.atIndexes && (textSubstring?.atIndexes | haslimittext:x);else nolimittext"> {{ textSubstring | limittext: data[dataKey] }} </section> <ng-template #nolimittext> <span>{{ data[dataKey] }}</span> </ng-template> </ng-template> </td> <!-- Buttons --> <td class="table-buttons" *ngIf="hasActionButtons && actionButtonsConfig?.position!='left'"> <span *ngFor="let aButton of actionButtons;let buttonIndex=index"> <button type="button" (click)="actionButtonClicked(i,buttonIndex)" class="btn {{ aButton?.class }}"> <div class="toltip" class="flx-tooltip" [ngClass]="{'flx-tooltip-left':aButton?.tooltipPosition=='left','flx-tooltip-bottom':aButton?.tooltipPosition=='bottom','flx-tooltip-right':aButton?.tooltipPosition=='right'}" *ngIf="aButton?.tooltip">{{ aButton?.tooltip }}</div> <span class="action-button-icon-left" [class]="aButton?.icon" *ngIf="!aButton?.iconPosition || aButton?.iconPosition!='right'"></span> <span class="button-text"> {{ aButton?.text }} </span> <span [class]="aButton?.icon" *ngIf="aButton?.iconPosition=='right'"></span> </button> </span> <input type="checkbox" checked (change)="addRemoveDeleteItem(data[multipleSelectKey],i,$event?.target?.checked)" *ngIf="enableMultipleSelection && service?.multipleDeletion?.length>0"> </td> </tr> <tr *ngIf="tData?.length<1"> <td colspan="10" class="text-center" *ngIf="service?.loadFinish"> <span style="color:#ff0000;font-size:13px;">No data found</span> <br> <button type="button" style="margin-top:1em;" (click)="reload()" class="btn btn-default {{ classes?.reloadbutton }}" color="primary"><span class="fa fa-refresh"></span> Reload</button> </td> </tr> </tbody> </table> </perfect-scrollbar> <div class="row"> <div class="col-md-12 rmpd flx-total-data" *ngIf="showBottomInfo"> <div class="row" *ngIf="!isSearching;else bottomInfoSearching"> <div class="col-md-4 text-left rmpd">Total pagination: <span> {{ service?.totalItems | ceil: limit }}</span></div> <div class="col-md-4 text-center rmpd"># of items per pagination: <span>{{ limit }}</span></div> <div class="col-md-4 text-right rmpd">Total items: <span>{{ (service?.flxData | async)?.length }}</span></div> </div> <ng-template #bottomInfoSearching> <div class="row"> <div class="col-md-12 text-right rmpd">Total search result: <span>{{ searchResultFound?.length }}</span></div> </div> </ng-template> </div> </div> </div> </div> </div> </div>`, styles: [`.flx-ui-datatable-main{background-color:#fff;padding-top:1em;padding-bottom:1em}.flx-ui-datatable-main .btn-danger{background-color:#f50057;border:1px solid #f50057;box-shadow:0 3px 5px 1px #ddd;-moz-box-shadow:0 3px 5px 1px #ddd;-webkit-box-shadow:0 3px 5px 1px #ddd;-o-box-shadow:0 3px 5px 1px #ddd;-ms-box-shadow:0 3px 5px 1px #ddd;border-radius:3px;margin-left:.3em}.flx-ui-datatable-main #flxdatatablebodyscrollbar,.flx-ui-datatable-main .table-responsive,.flx-ui-datatable-main .table-responsive table{width:100%}.flx-ui-datatable-main img.img-fall-back{width:30px;height:30px}.flx-ui-datatable-main .pagination-select input[type=text],.flx-ui-datatable-main .pagination-select select,.flx-ui-datatable-main .search-bar input[type=text],.flx-ui-datatable-main .search-bar select{border-top:none!important;border-left:none!important;border-right:none!important;border-bottom:2px solid #ddd!important;border-radius:0!important;box-shadow:0 0 0 0 transparent!important;-moz-box-shadow:0 0 0 0 transparent!important;-webkit-box-shadow:0 0 0 0 transparent!important;-o-box-shadow:0 0 0 0 transparent!important;-ms-box-shadow:0 0 0 0 transparent!important}.flx-ui-datatable-main .pagination-select input[type=text]:focus,.flx-ui-datatable-main .pagination-select select:focus,.flx-ui-datatable-main .search-bar input[type=text]:focus,.flx-ui-datatable-main .search-bar select:focus{border-bottom-color:#359!important;transition:.5s;-moz-transition:.5s;-webkit-transition:.5s;-o-transition:.5s;-ms-transition:.5s}.flx-ui-datatable-main .pagination-select select,.flx-ui-datatable-main .search-bar select{-webkit-appearance:none;appearance:none;-moz-appearance:none}.flx-ui-datatable-main .dropdown-toggle::after{display:none}.flx-ui-datatable-main .dropdown-menu-export{margin-left:-130px;padding-left:1em;padding-right:1em;width:100px!important;margin-top:1em;border:1px solid #ddd!important}.flx-ui-datatable-main .dropdown-menu-export::before{content:"";width:auto;height:auto;position:absolute;top:-20px;right:3px;border:10px solid;border-color:transparent transparent #ddd}.flx-ui-datatable-main .flx-datatable-pagination{padding-top:.5em}.flx-ui-datatable-main .flx-datatable-pagination button{width:35px!important;height:35px!important;border-radius:50em!important;border:none!important;box-shadow:0 3px 10px 0 #b3c4e6;-moz-box-shadow:0 3px 10px 0 #b3c4e6;-webkit-box-shadow:0 3px 10px 0 #b3c4e6;-o-box-shadow:0 3px 10px 0 #b3c4e6;-ms-box-shadow:0 3px 10px 0 #b3c4e6;background-color:#359;color:#fff;font-size:23px;font-weight:700;position:absolute;top:0}.flx-ui-datatable-main .flx-datatable-pagination button:first-child{left:0}.flx-ui-datatable-main .flx-datatable-pagination button:first-child .flx-datatable-tooltip-prev{position:absolute;left:0;font-size:13px;font-weight:400;color:#fff;background-color:#359;padding-left:.3em;padding-right:.3em;border-radius:3px;margin-left:-57px;margin-top:.3em;box-shadow:0 3px 10px 0 #b3c4e6!important;-moz-box-shadow:0 3px 10px 0 #b3c4e6!important;-webkit-box-shadow:0 3px 10px 0 #b3c4e6!important;-o-box-shadow:0 3px 10px 0 #b3c4e6!important;-ms-box-shadow:0 3px 10px 0 #b3c4e6!important;visibility:hidden}.flx-ui-datatable-main .flx-datatable-pagination button:hover>span.flx-datatable-tooltip-next,.flx-ui-datatable-main .flx-datatable-pagination button:hover>span.flx-datatable-tooltip-prev{visibility:visible}.flx-ui-datatable-main .flx-datatable-pagination .flx-pagination-end{background-color:#f50057!important;box-shadow:0 3px 10px 0 #ffc2d8!important;-moz-box-shadow:0 3px 10px 0 #ffc2d8!important;-webkit-box-shadow:0 3px 10px 0 #ffc2d8!important;-o-box-shadow:0 3px 10px 0 #ffc2d8!important;-ms-box-shadow:0 3px 10px 0 #ffc2d8!important;cursor:not-allowed}.flx-ui-datatable-main .flx-datatable-pagination .flx-pagination-end:hover>span.flx-datatable-tooltip-next,.flx-ui-datatable-main .flx-datatable-pagination .flx-pagination-end:hover>span.flx-datatable-tooltip-prev{visibility:hidden}.flx-ui-datatable-main .flx-datatable-pagination button:last-child{right:0}.flx-ui-datatable-main .flx-datatable-pagination button:last-child .flx-datatable-tooltip-next{position:absolute;left:0;font-size:13px;font-weight:400;color:#fff;background-color:#359;padding-left:.3em;padding-right:.3em;border-radius:3px;margin-left:35px;margin-top:.5em;box-shadow:0 3px 10px 0 #b3c4e6!important;-moz-box-shadow:0 3px 10px 0 #b3c4e6!important;-webkit-box-shadow:0 3px 10px 0 #b3c4e6!important;-o-box-shadow:0 3px 10px 0 #b3c4e6!important;-ms-box-shadow:0 3px 10px 0 #b3c4e6!important;visibility:hidden}.flx-ui-datatable-main .export-cnt button{border-radius:50em!important}.flx-ui-datatable-main .flx-tabl-container table.th-table{margin-top:.5em}.flx-ui-datatable-main table tbody tr{padding-top:0!important}.flx-ui-datatable-main table tbody tr td{padding-top:.5em;border-top:1px solid #f0f0f0;border-bottom:1px solid #f0f0f0}.flx-ui-datatable-main table tbody tr td button{margin-right:.3em;margin-left:0}.flx-ui-datatable-main table tbody tr td button div.flx-tooltip{position:absolute;background-color:rgba(32,27,27,.808);text-align:center;font-size:13px;color:#fff;border-radius:3px;box-shadow:0 3px 20px 0 #4b4949;-moz-box-shadow:0 3px 20px 0 #4b4949;-webkit-box-shadow:0 3px 20px 0 #4b4949;-o-box-shadow:0 3px 20px 0 #4b4949;-ms-box-shadow:0 3px 20px 0 #4b4949;margin-left:-2.5em;margin-top:-2.8em;visibility:hidden;width:80px;padding:.3em .5em}.flx-ui-datatable-main table tbody tr td button .flx-tooltip-left{margin-left:-95px!important;margin-top:-.3em!important}.flx-ui-datatable-main table tbody tr td button .flx-tooltip-bottom{margin-top:2.3em!important}.flx-ui-datatable-main table tbody tr td button .flx-tooltip-right{margin-left:28px!important;margin-top:-.3em!important}.flx-ui-datatable-main table tbody tr td button:hover>div.flx-tooltip{transition:.3s;visibility:visible}.flx-ui-datatable-main table tbody tr td.table-buttons{padding-top:.2em;padding-bottom:.2em}.flx-ui-datatable-main table tbody tr:nth-of-type(even){background-color:#f8f9fa}.flx-ui-datatable-main table tbody tr:nth-of-type(odd){background-color:#fff}.flx-ui-datatable-main .btn-danger:hover{background-color:#ff146b;border:1px solid #ff146b;box-shadow:0 3px 10px 1px #ff5fb6;-moz-box-shadow:0 3px 10px 1px #ff5fb6;-webkit-box-shadow:0 3px 10px 1px #ff5fb6;-o-box-shadow:0 3px 10px 1px #ff5fb6;-ms-box-shadow:0 3px 10px 1px #ff5fb6;transition:.5s;-moz-transition:.5s;-webkit-transition:.5s;-o-transition:.5s;-ms-transition:.5s}.flx-ui-datatable-main .btn-danger:focus{background-color:#f50057;border:1px solid #f50057}.flx-ui-datatable-main .btn-white{background-color:#fff}.flx-ui-datatable-main .btn-dark{background-color:#222!important}.flx-ui-datatable-main .btn-primary{background-color:#359;color:#fff;border:1px solid #359;box-shadow:0 3px 5px 1px #ddd;-moz-box-shadow:0 3px 5px 1px #ddd;-webkit-box-shadow:0 3px 5px 1px #ddd;-o-box-shadow:0 3px 5px 1px #ddd;-ms-box-shadow:0 3px 5px 1px #ddd;border-radius:3px}.flx-ui-datatable-main .btn-primary:hover{background-color:#4769ad;border:1px solid #4769ad;box-shadow:0 3px 10px 0 #b3c4e6;-moz-box-shadow:0 3px 10px 0 #b3c4e6;-webkit-box-shadow:0 3px 10px 0 #b3c4e6;-o-box-shadow:0 3px 10px 0 #b3c4e6;-ms-box-shadow:0 3px 10px 0 #b3c4e6;transition:.5s;-moz-transition:.5s;-webkit-transition:.5s;-o-transition:.5s;-ms-transition:.5s}.flx-ui-datatable-main .btn-primary:focus{background-color:#359;border:1px solid #359}.flx-ui-datatable-main .btn-large{padding-top:1em!important;padding-bottom:1em!important}.flx-ui-datatable-main .btn-medium{padding-top:.7em!important;padding-bottom:.7em!important}.flx-ui-datatable-main .btn-success{box-shadow:0 3px 5px 1px #ddd;-moz-box-shadow:0 3px 5px 1px #ddd;-webkit-box-shadow:0 3px 5px 1px #ddd;-o-box-shadow:0 3px 5px 1px #ddd;-ms-box-shadow:0 3px 5px 1px #ddd;border-radius:3px;background-color:#5cb85c;border:1px solid #5cb85c}.flx-ui-datatable-main .btn-success:hover{background-color:#70cc70;border:1px solid #70cc70;box-shadow:0 3px 10px 1px #9df99d;-moz-box-shadow:0 3px 10px 1px #9df99d;-webkit-box-shadow:0 3px 10px 1px #9df99d;-o-box-shadow:0 3px 10px 1px #9df99d;-ms-box-shadow:0 3px 10px 1px #9df99d;transition:.5s;-moz-transition:.5s;-webkit-transition:.5s;-o-transition:.5s;-ms-transition:.5s}.flx-ui-datatable-main .btn-success:focus{background-color:#5cb85c;border:1px solid #5cb85c}.flx-ui-datatable-main .btn-default{background-color:#fff;box-shadow:0 3px 5px 1px #eee;-moz-box-shadow:0 3px 5px 1px #eee;-webkit-box-shadow:0 3px 5px 1px #eee;-o-box-shadow:0 3px 5px 1px #eee;-ms-box-shadow:0 3px 5px 1px #eee;border-radius:3px;border:1px solid #ddd}.flx-ui-datatable-main .btn-default:hover{background-color:#fff;border:1px solid #e7e7e7;box-shadow:0 3px 10px 1px #e2e2e2;-moz-box-shadow:0 3px 10px 1px #e2e2e2;-webkit-box-shadow:0 3px 10px 1px #e2e2e2;-o-box-shadow:0 3px 10px 1px #e2e2e2;-ms-box-shadow:0 3px 10px 1px #e2e2e2;transition:.5s;-moz-transition:.5s;-webkit-transition:.5s;-o-transition:.5s;-ms-transition:.5s}.flx-ui-datatable-main .btn-default:focus{background-color:#fff;border:1px solid #ddd}.flx-ui-datatable-main .btn-secondary{box-shadow:0 3px 5px 1px #ddd;-moz-box-shadow:0 3px 5px 1px #ddd;-webkit-box-shadow:0 3px 5px 1px #ddd;-o-box-shadow:0 3px 5px 1px #ddd;-ms-box-shadow:0 3px 5px 1px #ddd;border-radius:3px;background-color:#1e88e5;color:#fff}.flx-ui-datatable-main .btn-secondary:hover{color:#fff;background-color:#2892ef;border:1px solid #2892ef;box-shadow:0 3px 10px 1px #55bfff;-moz-box-shadow:0 3px 10px 1px #55bfff;-webkit-box-shadow:0 3px 10px 1px #55bfff;-o-box-shadow:0 3px 10px 1px #55bfff;-ms-box-shadow:0 3px 10px 1px #55bfff;transition:.5s;-moz-transition:.5s;-webkit-transition:.5s;-o-transition:.5s;-ms-transition:.5s}.flx-ui-datatable-main .btn-secondary:focus{color:#fff}.flx-ui-datatable-main .pagination-button{background-color:#359;color:#fff}.flx-ui-datatable-main .table-font{font-family:Khula,sans-serif!important}.flx-ui-datatable-main .table-header-icon{position:absolute;right:.2em;width:80px;height:80px;font-size:50px;margin-top:-30px;border-radius:5px!important}.flx-ui-datatable-main .table-title{background-color:#359;color:#fff;border-radius:2px;padding:1em;font-size:15px;font-weight:700;margin-bottom:1.5em;font-family:Roboto,sans-serif;box-shadow:0 1px 5px 1px #ddd;-moz-box-shadow:0 1px 5px 1px #ddd;-webkit-box-shadow:0 1px 5px 1px #ddd;-o-box-shadow:0 1px 5px 1px #ddd;-ms-box-shadow:0 1px 5px 1px #ddd}.flx-ui-datatable-main .danger{background-color:#f50057;color:#fff}.flx-ui-datatable-main .primary{background-color:#359;color:#fff}.flx-ui-datatable-main .success{background-color:#5cb85c;color:#fff}.flx-ui-datatable-main .default{background-color:#fff;color:#000}.flx-ui-datatable-main .secondary{background-color:#1e88e5;color:#fff}.flx-ui-datatable-main .btn-clear{border:none!important;box-shadow:none!important}.flx-ui-datatable-main .input-group,.flx-ui-datatable-main .input-group input{background-color:transparent!important}.flx-ui-datatable-main .input-group-addon{b