ronaldkwandy

Untitled

May 17th, 2019
236
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import { Component, OnDestroy, Input, EventEmitter, forwardRef } from '@angular/core';
  2. import { NG_VALUE_ACCESSOR, NG_VALIDATORS, ControlValueAccessor, Validator, AbstractControl } from '@angular/forms';
  3.  
  4. declare var $: any;
  5. import '../../../../node_modules/jexcel/dist/js/jexcel';
  6. import '../../../../node_modules/jexcel/dist/js/jquery.jexcel';
  7. import '../../../../node_modules/jexcel/dist/js/jquery.jcalendar';
  8. import '../../../../node_modules/jexcel/dist/js/jquery.jdropdown';
  9. import '../../../../node_modules/jexcel/dist/js/jexcel-formulas';
  10. import '../../../../node_modules/jexcel/dist/js/jquery.csv.min';
  11.  
  12. import { DateFormatPipe } from '../pipe/date-format.pipe';
  13. import { JExcelColumn, JExcelError, JExcel, JExcelOptions, JExcelColumnRule } from './jexcel';
  14. import { ObjectHelper } from '../helper/object-helper';
  15.  
  16. import { TranslateService } from '@ngx-translate/core';
  17.  
  18. import * as lodash from 'lodash';
  19. import * as excelColumnName from 'excel-column-name';
  20.  
  21. export const JEXCEL_CONTROL_VALUE_ACCESSOR: any = {
  22.   provide: NG_VALUE_ACCESSOR,
  23.   useExisting: forwardRef(() => JExcelComponent),
  24.   multi: true
  25. };
  26.  
  27. export const JEXCEL_INPUT_VALIDATORS_ACCESSOR: any = {
  28.   provide: NG_VALIDATORS,
  29.   useExisting: forwardRef(() => JExcelComponent),
  30.   multi: true,
  31. };
  32.  
  33. @Component({
  34.   selector: 'app-jexcel',
  35.   templateUrl: './jexcel.component.html',
  36.   providers: [
  37.     JEXCEL_CONTROL_VALUE_ACCESSOR,
  38.     JEXCEL_INPUT_VALIDATORS_ACCESSOR
  39.   ]
  40. })
  41. export class JExcelComponent implements ControlValueAccessor, Validator, OnDestroy {
  42.  
  43.   @Input()
  44.   public get id(): string { return this._id; }
  45.   public set id(id: string) { this.setID(id); }
  46.   private _id: string;
  47.  
  48.   @Input()
  49.   public get name(): string { return this._name; }
  50.   public set name(name: string) { this._name = name; }
  51.   private _name: string;
  52.  
  53.   @Input()
  54.   public get options(): JExcelOptions { return this._options; }
  55.   public set options(options: JExcelOptions) {
  56.     this._options = options;
  57.     this.setSpreadsheet();
  58.   }
  59.   public _options: JExcelOptions;
  60.  
  61.   public value: any[] = [];
  62.   public onchange: EventEmitter<any[]>;
  63.   public translate: boolean = true;
  64.  
  65.   protected valid: boolean;
  66.   protected errors: any[];
  67.   protected globalErrors: JExcelError[] = [];
  68.  
  69.   private configuration: JExcel;
  70.   private dateFormatPipe: DateFormatPipe;
  71.   private defaultColumnWidth: number = 100;
  72.   private defaultCellBackground: string = 'background-color:white';
  73.   private defaultCellBackgroundError: string = 'background-color:lightcoral';
  74.   private defaultColumnType: any = { type: 'text' };
  75.   private onchangeEvent: Function;
  76.   private yearMonthPattern: string = '(0[1-9]|10|11|12)/20[0-9]{2}$';
  77.  
  78.   constructor(
  79.     private translateService: TranslateService
  80.   ) {
  81.     this.dateFormatPipe = new DateFormatPipe();
  82.     this.globalErrors = [];
  83.     this.errors = [];
  84.     this.onchange = new EventEmitter();
  85.  
  86.     this.onchangeEvent = (obj, cell, val) => {
  87.       const value = this.getValues();
  88.       this.propagateChange(value);
  89.       this.onchange.emit(value);
  90.     };
  91.  
  92.     this.setID('app-jexcel');
  93.   }
  94.  
  95.   ngOnDestroy() {
  96.     this.getInstance().jexcel('destroy');
  97.   }
  98.  
  99.   private setID(id: string) {
  100.     this._id = id;
  101.     this.getInstance().jexcel();
  102.   }
  103.  
  104.   public propagateChange = (_: any) => { /* do nothing */ };
  105.   public propagateTouch = (_: any) => { /* do nothing */ };
  106.  
  107.   public writeValue(value: any): void {
  108.     this.getInstance().jexcel('destroy');
  109.  
  110.     this.value = value;
  111.     if (this.configuration && this.value) {
  112.       this.initData();
  113.       this.getInstance().jexcel('setData', this.configuration.data, false);
  114.     }
  115.   }
  116.  
  117.   public registerOnChange(fn: (_: any) => {}): void {
  118.     this.propagateChange = fn;
  119.   }
  120.  
  121.   public registerOnTouched(fn: (_: any) => {}): void {
  122.     this.propagateTouch = fn;
  123.   }
  124.  
  125.   public setDisabledState(isDisabled: boolean): void { }
  126.  
  127.   public setSpreadsheet() {
  128.     this.globalErrors = [];
  129.     if (this.options) {
  130.       this.initConfiguration();
  131.       this.getInstance().jexcel(this.configuration);
  132.     }
  133.   }
  134.  
  135.   private getInstance() {
  136.     return $('#' + this.id);
  137.   }
  138.  
  139.   private initConfiguration() {
  140.     if (!this.configuration) {
  141.       this.configuration = {};
  142.     }
  143.  
  144.     this.configuration.colAlignments = [];
  145.     this.configuration.colHeaders = [];
  146.     this.configuration.colWidths = [];
  147.     this.configuration.columns = [];
  148.  
  149.     let index = 0;
  150.     for (const column of this.options.columns) {
  151.       if (ObjectHelper.isEmpty(column)) {
  152.         this.globalErrors.push({ code: 'jexcel.column.required', args: [(index + 1)] });
  153.         console.error('JExcel errors : ', this.globalErrors);
  154.         break;
  155.       }
  156.  
  157.       // Header text
  158.       if (ObjectHelper.isEmpty(column.headerText)) {
  159.         // TODO : set content alert LOL!
  160.         this.globalErrors.push({ code: 'jexcel.header.required', args: [(index + 1)] });
  161.         console.error('JExcel errors : ', this.globalErrors);
  162.         break;
  163.       } else {
  164.         if (this.translate) {
  165.           this.translateService.get(column.headerText).subscribe(
  166.             (translated) => {
  167.               this.configuration.colHeaders.push(translated);
  168.             }
  169.           );
  170.         } else {
  171.           this.configuration.colHeaders.push(column.headerText);
  172.         }
  173.       }
  174.  
  175.       // Width
  176.       if (ObjectHelper.isEmpty(column.width)) {
  177.         this.configuration.colWidths.push(this.defaultColumnWidth);
  178.       } else {
  179.         this.configuration.colWidths.push(column.width);
  180.       }
  181.  
  182.       // Alignment
  183.       if (ObjectHelper.isEmpty(column.alignment)) {
  184.         this.configuration.colAlignments.push('left');
  185.       } else {
  186.         this.configuration.colAlignments.push(column.alignment);
  187.       }
  188.  
  189.       // Column
  190.       const validColumn = lodash.cloneDeep(column);
  191.       if (ObjectHelper.isEmpty(validColumn.type)) {
  192.         validColumn.type = this.defaultColumnType;
  193.       }
  194.       if (ObjectHelper.isEmpty(validColumn.readOnly)) {
  195.         validColumn.readOnly = false;
  196.       }
  197.  
  198.       if (validColumn.type === 'dropdown' || validColumn.type === 'autocomplete') {
  199.         validColumn.source.map(item => {
  200.           if (typeof item === 'object') {
  201.             const name = validColumn.sourceKey ? validColumn.sourceKey : (Object.keys(item).find(key => key.indexOf('name') > -1 || key.indexOf('nama') > -1));
  202.             item.oid = item.id;
  203.             item.id = item[name];
  204.           }
  205.         });
  206.       }
  207.  
  208.       if (validColumn.type === 'yearmonth') {
  209.         validColumn.type = 'text';
  210.         validColumn.isYearMonth = true;
  211.       }
  212.  
  213.       this.configuration.columns.push(validColumn);
  214.  
  215.       // Data
  216.       this.initData();
  217.  
  218.       index++;
  219.     }
  220.  
  221.     // Options
  222.     this.initOptions();
  223.   }
  224.  
  225.   private initOptions() {
  226.     this.configuration.editable = this.options.editable;
  227.     this.configuration.columnResize = this.options.columnResize;
  228.     this.configuration.allowInsertRow = this.options.allowInsertRow;
  229.     this.configuration.allowManualInsertRow = this.options.allowManualInsertRow;
  230.     this.configuration.allowInsertColumn = this.options.allowInsertColumn;
  231.     this.configuration.allowManualInsertColumn = this.options.allowManualInsertColumn;
  232.     this.configuration.allowDeleteRow = this.options.allowDeleteRow;
  233.     this.configuration.allowDeleteColumn = this.options.allowDeleteColumn;
  234.     this.configuration.allowComments = this.options.allowComments;
  235.     this.configuration.toolbar = this.options.toolbar;
  236.     this.configuration.onchange = (this.options.onchange === undefined || this.options.onchange === null) ? this.onchangeEvent : this.options.onchange;
  237.   }
  238.  
  239.   private setConfig() {
  240.     Object.keys(this.configuration).forEach(config => {
  241.       this.getInstance().jexcel('setConfig', config, this.configuration[config]);
  242.     });
  243.   }
  244.  
  245.   public getValues() {
  246.     const value = lodash.cloneDeep(this.value);
  247.     const spreadsheetValues = this.getInstance().jexcel('getData', false) as any[];
  248.     spreadsheetValues.forEach((row: any[], rowIndex: number) => {
  249.       row.forEach((col: any, colIndex: number) => {
  250.         // TODO: blm ada handler untuk add row baru
  251.         value[rowIndex][this.configuration.columns[colIndex].columnName] = this.getColValue(this.configuration.columns[colIndex], col);
  252.       });
  253.     });
  254.     return value;
  255.   }
  256.  
  257.   private initData() {
  258.     if (this.configuration && this.value) {
  259.       const values: any = [];
  260.       let rowIndex = 0;
  261.       let colIndex = 0;
  262.       for (const obj of this.value) {
  263.         colIndex = 0;
  264.         values[rowIndex] = [];
  265.         for (const column of this.configuration.columns) {
  266.           if (ObjectHelper.isEmpty(obj[column.columnName])) {
  267.             values[rowIndex][colIndex] = '';
  268.           } else {
  269.             if (column.type === 'calendar') {
  270.               const dateVal = new Date(obj[column.columnName]);
  271.               values[rowIndex][colIndex] = this.dateFormatPipe.transform(dateVal, 'jexcelFullDate');
  272.             } else if (column.type === 'dropdown' || column.type === 'autocomplete') {
  273.               if (typeof obj[column.columnName] === 'object') {
  274.                 const name = column.sourceKey ? column.sourceKey : (Object.keys(obj[column.columnName]).find(key => key.indexOf('name') > -1 || key.indexOf('nama') > -1));
  275.                 values[rowIndex][colIndex] = obj[column.columnName][name];
  276.               } else {
  277.                 values[rowIndex][colIndex] = obj[column.columnName];
  278.               }
  279.             } else {
  280.               values[rowIndex][colIndex] = obj[column.columnName];
  281.             }
  282.           }
  283.           colIndex++;
  284.         }
  285.         rowIndex++;
  286.       }
  287.       this.configuration.data = values;
  288.     }
  289.   }
  290.  
  291.   private getColValue(column: JExcelColumn, value: any): any {
  292.     if (column) {
  293.       if (ObjectHelper.isEmpty(value)) {
  294.         return null;
  295.       } else {
  296.         if (column.type === 'text') {
  297.           if (!column.isYearMonth) {
  298.             return String(value);
  299.           } else {
  300.             const regExp = new RegExp(this.yearMonthPattern);
  301.             if (!regExp.test(value)) {
  302.               return value;
  303.             } else {
  304.               return String(value).substr(3, 4) + String(value).substr(0, 2);
  305.             }
  306.           }
  307.         } else if (column.type === 'numeric') {
  308.           return Number(value);
  309.         } else if (column.type === 'calendar') {
  310.           return new Date(value);
  311.         } else if (column.type === 'checkbox') {
  312.           return value === '1' ? true : false;
  313.         } else if (column.type === 'dropdown' || column.type === 'autocomplete') {
  314.           let objValue = lodash.cloneDeep(column.source.find(item => {
  315.             if (typeof item === 'object') {
  316.               const name = column.sourceKey ? column.sourceKey : (Object.keys(item).find(key => key.indexOf('name') > -1 || key.indexOf('nama') > -1));
  317.               return item[name] === value;
  318.             } else {
  319.               return item === value;
  320.             }
  321.           }));
  322.  
  323.           // Delete oid helper
  324.           if (typeof objValue === 'object') {
  325.             if (objValue.oid) {
  326.               objValue.id = objValue.oid;
  327.               delete (objValue.oid);
  328.             } else {
  329.               objValue = objValue.id;
  330.             }
  331.           }
  332.  
  333.           return objValue ? objValue : null;
  334.         } else {
  335.           return value;
  336.         }
  337.       }
  338.     }
  339.     return null;
  340.   }
  341.  
  342.   validate(c: AbstractControl): { [key: string]: any } {
  343.     if (this.configuration && this.value) {
  344.       const spreadsheetValues = this.getInstance().jexcel('getData', false) as any[];
  345.       spreadsheetValues.forEach((rowValue, rowIndex) => {
  346.         if (!this.errors[rowIndex]) {
  347.           this.errors[rowIndex] = { row: rowIndex, cells: [] };
  348.         }
  349.  
  350.         this.configuration.columns.forEach((column, colIndex) => {
  351.           const cell = { cell: colIndex, validation: null, code: null, args: [] };
  352.  
  353.           if (column.validations) {
  354.             for (const validation of column.validations) {
  355.               if (validation.rule === JExcelColumnRule.required) {
  356.                 if (ObjectHelper.isEmpty(rowValue[colIndex])) {
  357.                   cell.validation = { required: true };
  358.                   this.setStyle(rowIndex, colIndex, this.defaultCellBackgroundError);
  359.  
  360.                   cell.code = 'excel.cell.required';
  361.                   this.translateService.get(cell.code).subscribe(
  362.                     (translateMessage) => {
  363.                       this.setComment(rowIndex, colIndex, translateMessage);
  364.                     }
  365.                   );
  366.                 } else {
  367.                   this.setStyle(rowIndex, colIndex, this.defaultCellBackground);
  368.                   this.setComment(rowIndex, colIndex, '');
  369.                 }
  370.               }
  371.  
  372.               if (validation.rule === JExcelColumnRule.min) {
  373.                 if (column.type === 'text') {
  374.                   if (String(rowValue[colIndex]).length < validation.value) {
  375.                     cell.validation = { min: validation.value, actualValue: rowValue[colIndex] };
  376.                     this.setStyle(rowIndex, colIndex, this.defaultCellBackgroundError);
  377.  
  378.                     const colName = excelColumnName.intToExcelCol(colIndex + 1);
  379.                     cell.code = 'excel.cell.min.length';
  380.                     cell.args = [validation.value];
  381.                     this.translateService.get(cell.code, cell.args).subscribe(
  382.                       (translateMessage) => {
  383.                         this.setComment(rowIndex, colIndex, translateMessage);
  384.                       }
  385.                     );
  386.                   } else {
  387.                     this.setStyle(rowIndex, colIndex, this.defaultCellBackground);
  388.                     this.setComment(rowIndex, colIndex, '');
  389.                   }
  390.                 } else if (column.type === 'numeric') {
  391.                   if (Number(rowValue[colIndex]) < validation.value) {
  392.                     cell.validation = { min: validation.value, actualValue: Number(rowValue[colIndex]) };
  393.                     this.setStyle(rowIndex, colIndex, this.defaultCellBackgroundError);
  394.  
  395.                     const colName = excelColumnName.intToExcelCol(colIndex + 1);
  396.                     cell.code = 'excel.cell.min.length';
  397.                     cell.args = [validation.value];
  398.                     this.translateService.get(cell.code, cell.args).subscribe(
  399.                       (translateMessage) => {
  400.                         this.setComment(rowIndex, colIndex, translateMessage);
  401.                       }
  402.                     );
  403.                   } else {
  404.                     this.setStyle(rowIndex, colIndex, this.defaultCellBackground);
  405.                     this.setComment(rowIndex, colIndex, '');
  406.                   }
  407.                 } else if (column.type === 'calendar') {
  408.                   const dateValue = new Date(rowValue[colIndex]);
  409.                   if (dateValue.getTime() < (validation.value as Date).getTime()) {
  410.                     cell.validation = { min: validation.value, actualValue: dateValue };
  411.                     this.setStyle(rowIndex, colIndex, this.defaultCellBackgroundError);
  412.  
  413.                     cell.code = 'excel.cell.min.length';
  414.                     cell.args = [validation.value];
  415.                     this.translateService.get(cell.code, cell.args).subscribe(
  416.                       (translateMessage) => {
  417.                         this.setComment(rowIndex, colIndex, translateMessage);
  418.                       }
  419.                     );
  420.                   } else {
  421.                     this.setStyle(rowIndex, colIndex, this.defaultCellBackground);
  422.                     this.setComment(rowIndex, colIndex, '');
  423.                   }
  424.                 } else {
  425.                   cell.validation = null;
  426.                   this.setStyle(rowIndex, colIndex, this.defaultCellBackground);
  427.                   this.setComment(rowIndex, colIndex, '');
  428.                 }
  429.               }
  430.  
  431.               if (validation.rule === JExcelColumnRule.max) {
  432.                 if (column.type === 'text') {
  433.                   if (String(rowValue[colIndex]).length > validation.value) {
  434.                     cell.validation = { max: validation.value, actualValue: rowValue[colIndex] };
  435.                     this.setStyle(rowIndex, colIndex, this.defaultCellBackgroundError);
  436.  
  437.                     cell.code = 'excel.cell.max.length';
  438.                     cell.args = [validation.value];
  439.                     this.translateService.get(cell.code, cell.args).subscribe(
  440.                       (translateMessage) => {
  441.                         this.setComment(rowIndex, colIndex, translateMessage);
  442.                       }
  443.                     );
  444.                   } else {
  445.                     this.setStyle(rowIndex, colIndex, this.defaultCellBackground);
  446.                     this.setComment(rowIndex, colIndex, '');
  447.                   }
  448.                 } else if (column.type === 'numeric') {
  449.                   if (Number(rowValue[colIndex]) > validation.value) {
  450.                     cell.validation = { max: validation.value, actualValue: Number(rowValue[colIndex]) };
  451.                     this.setStyle(rowIndex, colIndex, this.defaultCellBackgroundError);
  452.  
  453.                     cell.code = 'excel.cell.max.length';
  454.                     cell.args = [validation.value];
  455.                     this.translateService.get(cell.code, cell.args).subscribe(
  456.                       (translateMessage) => {
  457.                         this.setComment(rowIndex, colIndex, translateMessage);
  458.                       }
  459.                     );
  460.                   } else {
  461.                     this.setStyle(rowIndex, colIndex, this.defaultCellBackground);
  462.                     this.setComment(rowIndex, colIndex, '');
  463.                   }
  464.                 } else if (column.type === 'calendar') {
  465.                   const dateValue = new Date(rowValue[colIndex]);
  466.                   if (dateValue.getTime() > (validation.value as Date).getTime()) {
  467.                     cell.validation = { max: validation.value, actualValue: dateValue };
  468.                     this.setStyle(rowIndex, colIndex, this.defaultCellBackgroundError);
  469.  
  470.                     cell.code = 'excel.cell.max.length';
  471.                     cell.args = [validation.value];
  472.                     this.translateService.get(cell.code, cell.args).subscribe(
  473.                       (translateMessage) => {
  474.                         this.setComment(rowIndex, colIndex, translateMessage);
  475.                       }
  476.                     );
  477.                   } else {
  478.                     this.setStyle(rowIndex, colIndex, this.defaultCellBackground);
  479.                     this.setComment(rowIndex, colIndex, '');
  480.                   }
  481.                 } else {
  482.                   cell.validation = null;
  483.                   this.setStyle(rowIndex, colIndex, this.defaultCellBackground);
  484.                   this.setComment(rowIndex, colIndex, '');
  485.                 }
  486.               }
  487.  
  488.               if (validation.rule === JExcelColumnRule.yearmonth) {
  489.                 const regExp = new RegExp(this.yearMonthPattern);
  490.                 if (!regExp.test(rowValue[colIndex])) {
  491.                   cell.validation = { pattern: validation.value };
  492.                   this.setStyle(rowIndex, colIndex, this.defaultCellBackgroundError);
  493.  
  494.                   cell.code = 'excel.cell.invalid';
  495.                   cell.args = [validation.value];
  496.                   this.translateService.get(cell.code).subscribe(
  497.                     (translateMessage) => {
  498.                       this.setComment(rowIndex, colIndex, translateMessage);
  499.                     }
  500.                   );
  501.                 } else {
  502.                   cell.validation = null;
  503.                   this.setStyle(rowIndex, colIndex, this.defaultCellBackground);
  504.                   this.setComment(rowIndex, colIndex, '');
  505.                 }
  506.               }
  507.  
  508.               if (validation.rule === JExcelColumnRule.pattern) {
  509.                 const regExp = new RegExp(String(validation.value));
  510.                 if (!regExp.test(rowValue[colIndex])) {
  511.                   cell.validation = { pattern: validation.value };
  512.                   this.setStyle(rowIndex, colIndex, this.defaultCellBackgroundError);
  513.  
  514.                   cell.code = 'excel.cell.invalid';
  515.                   cell.args = [validation.value];
  516.                   this.translateService.get(cell.code).subscribe(
  517.                     (translateMessage) => {
  518.                       this.setComment(rowIndex, colIndex, translateMessage);
  519.                     }
  520.                   );
  521.                 } else {
  522.                   cell.validation = null;
  523.                   this.setStyle(rowIndex, colIndex, this.defaultCellBackground);
  524.                   this.setComment(rowIndex, colIndex, '');
  525.                 }
  526.               }
  527.  
  528.               if (this.errors[rowIndex].cells[colIndex]) {
  529.                 this.errors[rowIndex].cells[colIndex] = cell;
  530.               } else {
  531.                 this.errors[rowIndex].cells.push(cell);
  532.               }
  533.             }
  534.           } else {
  535.             if (!this.errors[rowIndex].cells[colIndex]) {
  536.               this.errors[rowIndex].cells.push(cell);
  537.             }
  538.           }
  539.         });
  540.       });
  541.  
  542.       this.valid = true;
  543.       for (const error of this.errors) {
  544.         if (error.cells.find(item => item.validation !== null) !== undefined) {
  545.           this.valid = false;
  546.           break;
  547.         }
  548.       }
  549.  
  550.       if (this.valid) {
  551.         return null;
  552.       } else {
  553.         return this.errors;
  554.       }
  555.     }
  556.     return null;
  557.   }
  558.  
  559.   public setStyle(rowIndex: number, colIndex: number, style: string) {
  560.     const colName = excelColumnName.intToExcelCol(colIndex + 1);
  561.     const cell = {};
  562.     cell[colName + '' + (rowIndex + 1)] = style;
  563.     this.getInstance().jexcel('setStyle', [cell]);
  564.   }
  565.  
  566.   public setComment(rowIndex: number, colIndex: number, comment: string = '') {
  567.     const colName = excelColumnName.intToExcelCol(colIndex + 1);
  568.     this.getInstance().jexcel('setComments', colName + '' + (rowIndex + 1), comment);
  569.   }
  570.  
  571.   public updateSettings(options: JExcelOptions) {
  572.     this.getInstance().jexcel('destroy');
  573.     this.options = Object.assign({}, options);
  574.  
  575.     this.globalErrors = [];
  576.     if (this.options) {
  577.       this.initConfiguration();
  578.       this.setConfig();
  579.     }
  580.   }
  581. }
Advertisement
Add Comment
Please, Sign In to add comment