flx-ui-datatable
Version: 
## AUTHOR Felix Kakra Acheampong from (`Orcons Systems`) Ghana
953 lines (939 loc) • 176 kB
JavaScript
import { Injectable, Component, Input, Output, EventEmitter, Directive, ElementRef, Pipe, NgModule } from '@angular/core';
import { Http, Headers, HttpModule } from '@angular/http';
import { map, retry } from 'rxjs/operators';
import { BehaviorSubject } from 'rxjs';
import { Validators, FormBuilder, ReactiveFormsModule, FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { PerfectScrollbarModule, PERFECT_SCROLLBAR_CONFIG } from 'ngx-perfect-scrollbar';
/**
 * @fileoverview added by tsickle
 * @suppress {checkTypes} checked by tsc
 */
class FlxUiDatatableService {
    /**
     * @param {?} http
     */
    constructor(http) {
        this.http = http;
        this.dataUrl = '';
        this.behavior = new BehaviorSubject([]);
        this.flxData = this.behavior.asObservable();
        this.pagination = [];
        this.totalItems = 0;
        this.dataOffset = 0;
        this.limit = 20;
        this.dataSrcKey = '';
        //Hold items selected for multiple select
        this.multipleDeletion = [];
        //Keep track if API call is completed
        this.loadFinish = false;
        this.lazyloadingConfig = {};
        this.processedData = {};
    }
    /**
     * @param {?} config
     * @return {?}
     */
    setLazyloadingConfig(config) {
        this.lazyloadingConfig = config;
    }
    /**
     *
     * @param {?} url User api rul
     * @return {?}
     */
    getData(url) {
        let /** @type {?} */ headers = new Headers();
        headers.append('Content-Type', 'application/x-www-form-urlencoded');
        return this.http.get(url, { headers: headers }).pipe(retry(5), map((response) => response.json()));
    }
    /**
     *
     * @param {?} url Service api url
     * @param {?} id Datatype id to export
     * @param {?} data Data to export
     * @return {?}
     */
    postData(url, id, data) {
        let /** @type {?} */ headers = new Headers();
        headers.append('Content-Type', 'application/json; charset=utf-8');
        return this.http.post(url + id, data, { headers: headers }).pipe(map((resp) => resp.json()));
    }
    /**
     *
     * @param {?} dataUrl Set dataurl
     * @return {?}
     */
    setDataUrl(dataUrl) {
        this.dataUrl = dataUrl;
    }
    /**
     * @return {?}
     */
    getDataUrl() {
        return this.dataUrl;
    }
    /**
     *
     * @param {?} data Register new data from user API
     * @return {?}
     */
    chageDataTable(data) {
        this.behavior.next(data);
    }
    /**
     *
     * @param {?} numberOfList Total number of list
     * @param {?} limit Pagination limit
     * @return {?}
     */
    createPagination(numberOfList, limit) {
        let /** @type {?} */ obj = [];
        let /** @type {?} */ counter = 1;
        for (let /** @type {?} */ i = 0; i < numberOfList; i += limit) {
            obj.push({ label: counter, value: i });
            counter++;
        }
        if (!this.isLazyLoadingEnabled) {
            obj.push({ label: 'All', value: 'all' });
        }
        return obj;
    }
    /**
     * @return {?}
     */
    isLazyLoadingEnabled() {
        return this.lazyloadingConfig.hasOwnProperty("apiOffsetKey") && this.lazyloadingConfig.apiOffsetKey;
    }
    /**
     * @param {?} dataUrl
     * @param {?=} setSelectPagination
     * @return {?}
     */
    loadFlxDataTableData(dataUrl, setSelectPagination = true) {
        this.loadFinish = false;
        this.loader = this.getData(dataUrl).subscribe((responseData) => {
            try {
                this.multipleDeletion = [];
                var /** @type {?} */ data = (this.dataSrcKey) ? responseData[this.dataSrcKey] : responseData;
                this.chageDataTable(data);
                if (this.isLazyLoadingEnabled()) {
                    this.totalItems = responseData.total;
                    // Handle 1 pagination out of zero problem 1/0  instead of 0/0 if no data is comming
                    if (data.length > 0) {
                        this.dataOffset = this.dataOffset + this.limit;
                    }
                }
                else {
                    this.totalItems = data.length;
                    this.dataOffset = 1;
                }
                if (setSelectPagination) {
                    if (this.isLazyLoadingEnabled()) {
                        this.pagination = this.createPagination(responseData.total, this.limit);
                    }
                    else {
                        this.pagination = this.createPagination(data.length, this.limit);
                    }
                }
                this.loadFinish = true;
            }
            catch (/** @type {?} */ e) {
                console.log('Error in reading data in ', e);
            }
        }, (e => {
            this.loadFinish = true;
        }));
    }
    /**
     * @return {?}
     */
    cancelLoading() {
        this.loader.unsubscribe();
    }
    /**
     * @param {?} srcKey
     * @return {?}
     */
    setDataSrcKey(srcKey) {
        this.dataSrcKey = srcKey;
    }
    /**
     * @return {?}
     */
    getDataLength() {
        return new Promise((resolve) => {
            this.flxData.subscribe((resp) => {
                resolve(resp.length);
            }, (e => {
                resolve(0);
            }));
        });
    }
}
FlxUiDatatableService.decorators = [
    { type: Injectable },
];
/** @nocollapse */
FlxUiDatatableService.ctorParameters = () => [
    { type: Http }
];
/**
 * @fileoverview added by tsickle
 * @suppress {checkTypes} checked by tsc
 */
class FlxUiDataTable {
    /**
     * @param {?} service
     */
    constructor(service) {
        this.service = service;
        this.behavior = new BehaviorSubject([]);
        this.flxDatatableData = this.behavior.asObservable();
        this.service.flxData.subscribe((resp) => {
            this.ChangeData(resp);
        });
    }
    /**
     *
     * @param {?} data Change table data with new data
     * @return {?}
     */
    ChangeData(data) {
        this.behavior.next(data);
    }
    /**
     * Reload data from api: void
     * @return {?}
     */
    reloadData() {
        this.service.loadFlxDataTableData(this.service.getDataUrl());
    }
    /**
     * @return {?}
     */
    abortRequest() {
        this.service.cancelLoading();
    }
}
FlxUiDataTable.decorators = [
    { type: Injectable },
];
/** @nocollapse */
FlxUiDataTable.ctorParameters = () => [
    { type: FlxUiDatatableService }
];
/**
 * @fileoverview added by tsickle
 * @suppress {checkTypes} checked by tsc
 */
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 {
                for (let /** @type {?} */ i = 0; i < this.tData.length; i++) {
                    obj.push(this.tData[i]);
                }
                // 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) => {
                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