swimlane / ngx-datatable

✨ A feature-rich yet lightweight data-table crafted for Angular
http://swimlane.github.io/ngx-datatable/
MIT License
4.63k stars 1.68k forks source link

How to stop moving checkboxes to right corner when the property [frozenLeft]=“true” in Angular 08 NGX data table #1899

Open ksanuradha opened 4 years ago

ksanuradha commented 4 years ago

I wan't to enable Freeze Column left and Unfreeze Column feature in my Angular 09 project which I am using Angular Material and NGX data-table. I developed it by enabling the NGX data-table property [frozenLeft]="true" but there is a draw back in my solution. When I right click on a column header and make it freeze selected column is freeze but checkboxes moved to right conner. I wan't to fixed the checkboxes(both header and column) left hand corner in the table. (Images are attached to the problem)

<div block="data-table">
      <ngx-datatable elem="table" class="material"
                     #dataTable
                     [rows]="dataSource"
                     [columns]="columns"
                     [columnMode]="columnMode"
                     [headerHeight]="headerHeight"
                     [footerHeight]="footerHeight"
                     [rowHeight]="rowHeight"
                     [rowClass]="getRowClass"
                     [limit]="rowsPerPage"
                     [scrollbarV]="scrollbarV"
                     [scrollbarH]="scrollbarH"
                     [selected]="selected"
                     [selectionType]="selectionType"
                     [displayCheck]="displayCheckbox"
                     [externalSorting]="isExternalSorting"
                     (select)="onSelect($event)"
                     (activate)="onActivate($event)"
                     (sort)="onSort($event)">

        <ngx-datatable-column elem="column"
                              [width]="30"
                              [sortable]="false"
                              [canAutoResize]="false"
                              [draggable]="false"
                              [resizeable]="false"
                              [headerCheckboxable]="true"
                              [checkboxable]="true">
        </ngx-datatable-column>

        <ngx-datatable-column elem="column" *ngFor="let col of columns; let i = index"
                              [name]="col.name"
                              [prop]="col.prop"
                              [sortable]="isExternalSorting"
                              [cellTemplate]="col.cellTemplate"
                              [headerTemplate]="col.headerTemplate"
                              [frozenLeft]="colNames[i]">

          <ng-template ngx-datatable-header-template let-sort="sortFn" >
            <div (click)= "sort()" (contextmenu)="isRightClick ? onRightClick($event,col.prop) : null">
              {{ col.name }}
            </div>
          </ng-template>

          <ng-template let-value="value" ngx-datatable-cell-template let-rowIndex="rowIndex" let-row="row">
            <div *ngIf="secondaryInlineEditing && isRowEditable && rowIndex === editableRowIndex; else nonEditable" elem="cell-editable">
              <mat-form-field elem="form-field"  [mod]="{editable:(isRowEditable && rowIndex === editableRowIndex)}" >
                <div *ngIf="col.inlineEditingType === 'INPUT_ITEM'">
                  <input #htmlInputElement matInput type="text" [(ngModel)]="row[col.prop]" [disabled]="!(isRowEditable && rowIndex === editableRowIndex)" elem="input" [mod]="{editable:(isRowEditable && rowIndex === editableRowIndex)}"/>
                  <mat-icon matSuffix *ngIf="isRowEditable && rowIndex === editableRowIndex" (click)="htmlInputElement.value = editingRowKeys[rowIndex][col.prop]" elem="clear-icon">cancel</mat-icon>
                </div>
                <div *ngIf="col.inlineEditingType === 'SELECT_ITEM'">
                  <mat-select #htmlSelectElement [(ngModel)]="row[col.prop]" [disabled]="!(isRowEditable && rowIndex === editableRowIndex)" elem="select-item" [mod]="{editable:(isRowEditable && rowIndex === editableRowIndex)}">
                    <mat-option *ngFor="let option of col.selectItemOptions" [value]="option.value">
                      {{option.label}}
                    </mat-option>
                  </mat-select>
    <!--              To Do when custom select item developed - DO NOT REMOVE-->
    <!--              <mat-icon matSuffix *ngIf="isRowEditable && rowIndex === editableRowIndex" (click)="htmlSelectElement.value = value">cancel</mat-icon>-->
                </div>
                <div *ngIf="col.inlineEditingType === 'DATE_PICKER_ITEM'">
                  <input matInput [(ngModel)]="row[col.prop]" [matDatepickerFilter]="customDateFilter" [matDatepicker]="picker" [disabled]="!(isRowEditable && rowIndex === editableRowIndex)"  elem="date-picker" [mod]="{editable:(isRowEditable && rowIndex === editableRowIndex)}">
                  <mat-datepicker-toggle matSuffix [for]="picker" elem="picker-toggle"></mat-datepicker-toggle>
                  <mat-datepicker #picker elem="picker"></mat-datepicker>
                </div>
              </mat-form-field>
            </div>
            <ng-template #nonEditable>
              {{ value }}
            </ng-template>
          </ng-template>
        </ngx-datatable-column >
        <ngx-datatable-column [width]="30" elem="column" mod="editable" *ngIf="secondaryInlineEditing">
          <ng-template let-value="value" ngx-datatable-cell-template let-rowIndex="rowIndex" let-row="row">
            <div *ngIf="!isRowEditable; else rowEditable">
              <mat-icon elem="icon" mod="edit" (click)="onRowEditable(row, rowIndex)">edit</mat-icon>
            </div>
            <ng-template #rowEditable>
              <mat-icon *ngIf="rowIndex === editableRowIndex" elem="icon" mod="save" (click)="onSaveEdit(row, rowIndex)">check_circle_outline</mat-icon>
              <mat-icon *ngIf="rowIndex === editableRowIndex" elem="icon" mod="cancel" (click)="onCancelEdit(row, rowIndex)">add_circle_outline</mat-icon>
            </ng-template>
          </ng-template>
        </ngx-datatable-column>

        <ngx-datatable-footer elem="footer" *ngIf="isFooterAllowed">
          <ng-template
            ngx-datatable-footer-template
            let-rowCount="rowCount"
            let-pageSize="pageSize"
            let-selectedCount="selectedCount"
            let-curPage="curPage"
            let-offset="offset">
            <div elem="footer-text" style="padding: 5px 10px">
              <div *ngIf="selectedCount; else notSelectedCount">
                {{selectedCount}} selected of {{rowCount}}
              </div>
              <ng-template #notSelectedCount>
                {{(pageSize * offset) + 1}} - {{pageSize * curPage}} of {{rowCount}}
              </ng-template>
            </div>
            <datatable-pager elem="pagination"
              [pagerLeftArrowIcon]="'datatable-icon-left'"
              [pagerRightArrowIcon]="'datatable-icon-right'"
              [pagerPreviousIcon]="'datatable-icon-prev'"
              [pagerNextIcon]="'datatable-icon-skip'"
              [page]="curPage"
              [size]="pageSize"
              [count]="rowCount"
              [hidden]="!((rowCount / pageSize) > 1)"
              (change)="dataTable.onFooterPage($event)">
            </datatable-pager>
          </ng-template>
        </ngx-datatable-footer>
      </ngx-datatable>

    </div>

    <div class="contextmenu"  [ngStyle]="{'left.px': x, 'top.px': y}" [ngClass]="{'d-none': !isShow}">
      <a class="dropdown-item context-menu" [ngStyle]="{'display': onLockDiv}" (click)="onLock(rightClickColumn)">Freeze Column</a> <br>
      <a class="dropdown-item context-menu" [ngStyle]="{'display': onUnLockDtv}" (click)="onUnLock(rightClickColumn)">Unfreeze Column</a>
    </div>
import {AfterViewInit, Component, EventEmitter, Input, OnInit, Output, HostListener} from '@angular/core';
    import {ColumnMode, SelectionType, TableColumn} from '@swimlane/ngx-datatable';
    import {CustomTableColumn} from '@app/shared/models/dashboard/custom-table-column.model';

    @Component({
      selector: 'app-ngx-data-table',
      templateUrl: './data-table.component.html',
      styleUrls: ['./data-table.component.scss']
    })
    export class DataTableComponent implements OnInit, AfterViewInit {

      /**
       * Rows that are displayed in the table.
       */
      @Input() dataSource: any[] = [];
      /**
       * Columns to be displayed.
       */
      @Input() columns: CustomTableColumn[];
      /**
       * Type of column width distribution formula.
       * Example: flex, force, standard
       */
      @Input() columnMode: ColumnMode | keyof typeof ColumnMode;
      /**
       * The page size to be shown.
       * Default value: `undefined`
       */
      @Input() rowsPerPage: number;
      /**
       * List of row objects that should be
       * represented as selected in the grid.
       * Default value: `[]`
       */
      @Input() selected: any[] = [];
      /**
       * Type of row selection. Options are:
       *  - `single`
       *  - `multi`
       *  - `checkbox`
       *  - `multiClick`
       *  - `cell`
       * For no selection pass a `falsey`.
       * Default value: `undefined`
       */
      @Input() selectionType: SelectionType;

      /**
       * Footer should be set or not
       * Default value is false
       */
      @Input() isFooterAllowed = false;
      /**
       * isExternalSorting should be set or not
       * Default value is false
       */
      @Input() isExternalSorting = false;

      /**
       * A function you can use to check whether you want
       * to show the checkbox for a particular row based on a criteria. Example:
       *
       *    (row, column, value) => {
       *      return row.name !== 'Ethel Price';
       *    }
       */
      @Input() displayCheckbox: (row: any, column?: any, value?: any) => boolean;

      /**
       * isExternalSorting should be set to /true
       * isThirdClickDisable true remove the sort in the 3rd user click
       */
      @Input() isThirdClickDisable = true;

      /**
       * isRightClick responsible for Frozen or Unfrozen Columns
       * Default value is false
       *
       */
      @Input() isRightClick = false ;

      /**
       * A cell or row was selected.
       */
      @Output() selectRow: EventEmitter<any> = new EventEmitter();
      /**
       * A cell or row was focused via keyboard or mouse click/hover.
       */
      @Output() activate: EventEmitter<any> = new EventEmitter();

      @Output() sort: EventEmitter<{
        currentShorting: string, priviousShorting: string, columnName: string, serversideSorting: boolean,
        isThirdClick: boolean
      }> = new EventEmitter();

      @Input() secondaryInlineEditing = false;
      @Output() editableRow: EventEmitter<any> = new EventEmitter();
      @Output() saveEdit: EventEmitter<any> = new EventEmitter();
      @Output() cancelEdit: EventEmitter<any> = new EventEmitter();

      // Start Inline Editing
      @Input() editingRowKeys: { [s: string]: any; } = {};
      @Input() customDateFilter: (date?: Date) => boolean;

      editableRowIndex: number;
      isRowEditable: boolean;
      // End Inline Editing

      headerHeight = 40;
      footerHeight = 0;
      // rowHeight = 36;
      scrollbarV = false;
      scrollbarH = true;
      serversideSorting: boolean;
      currentShorting: string;
      priviousShorting: string;
      columnName: string;
      isThirdClick = false;
      count = 0;
      prePropValue = '';
      sortingAlgorithms = ['asc', 'desc', 'desc'];
      public x: any;
      public y: any;
      public isShow = false;
      colNames = [false, false, false, false];
      rightClickColumn: string;
      onLockDiv = 'none';
      onUnLockDtv = 'none';

      @HostListener('document:click', ['$event'])
      private clickedOutside($event) {
        if ($event.target.className !== 'dropdown-item context-menu') {
          this.isShow = false;
        }
      }
      private onLock(colname: string): void {
        console.log('onLock');
        console.log(colname);
        const tableColumn = colname === 'name' ? 0 : colname === 'age' ? 1 : colname === 'gender' ? 2 : 3 ;
        this.colNames[tableColumn] = true;
        this.isShow = false;
      }

      private onUnLock(colname: string): void {
        console.log('onUnLock');
        console.log(colname);
        const tableColumn = colname === 'name' ? 0 : colname === 'age' ? 1 : colname === 'gender' ? 2 : 3 ;
        this.colNames[tableColumn] = false;
        this.isShow = false;
      }

      private onRightClick(event, colName: string) {
        event.preventDefault();
        this.x = event.pageX;
        this.y = event.pageY - 60;
        //  this.x = 120;
        //  this.y = 100;
        const tableColumn = colName === 'name' ? 0 : colName === 'age' ? 1 : colName === 'gender' ? 2 : 3 ;
        this.onLockDiv = this.colNames[tableColumn] ? 'none' : 'block' ;
        this.onUnLockDtv = this.colNames[tableColumn] ? 'block' : 'none' ;
        this.rightClickColumn = colName;
        this.isShow = true;
      }

      constructor() { }

      ngOnInit(): void {
        this.setDefaultValues();
      }

      setDefaultValues() {
        if (this.isFooterAllowed) {
          this.footerHeight = 44;
        }
      }

      onSelect(event): void {
        this.selectRow.emit(event);
      }

      onActivate(event): void {
        this.activate.emit(event);
      }

      ngAfterViewInit(): void {

      }

      onSort(event): void {
        // event was triggered, start sort sequence
        setTimeout(() => {
          const rows = [...this.dataSource];
          // this is only for demo purposes, normally
          // your server would return the result for
          // you and you would just set the rows prop
          const sort = this.isThirdClickDisable ? this.removeSort(event) : this.defaultSort(event);
          rows.sort((a, b) => {
            // console.log(b[sort.prop] == undefined);
            // console.log(a[sort.prop] == undefined);
              return a[sort.prop].localeCompare(b[sort.prop]) * (sort.dir === 'desc' ? -1 : 1);
          });
          this.dataSource = rows;
        }, 10);
      }

      private removeSort(changeShorts: any): any {
        // select different column
        if ((this.prePropValue !== changeShorts.sorts[0].prop)) {
          this.count = 0;
          this.isThirdClick = false;
          this.messageDisplay(this.sortingAlgorithms[this.count], changeShorts.prevValue, changeShorts.column.name);
        }
        changeShorts.sorts[0].dir = this.sortingAlgorithms[this.count];

        if ( this.count === 2 ) {
          // This is the tird click
          // Implement the logic for remove sort.(Refetch the search criteria)
          this.serversideSorting = false;
          this.isThirdClick = true;
          this.messageDisplay('', '', '');
          this.count = 0;
        } else {
            this.isThirdClick = false;
            this.messageDisplay(this.sortingAlgorithms[this.count], changeShorts.prevValue, changeShorts.column.name);
            this.count++;

        }
        this.prePropValue = changeShorts.sorts[0].prop;
        return changeShorts.sorts[0];
      }

      private messageDisplay(currentShorting: string, priviousShorting: string, columnName: string) {
        if (priviousShorting === undefined) {
            this.priviousShorting = `prevValue : ${priviousShorting}`;
        } else {
          const c = currentShorting === 'asc' ? 'desc' : 'asc';
          this.priviousShorting = `prevValue : ${c}`;
        }
        this.serversideSorting = true;
        this.currentShorting = `newValue : ${currentShorting}`;
        this.columnName = `column name : ${columnName}`;

        this.sort.emit({
          currentShorting : this.currentShorting,
          priviousShorting : this.priviousShorting,
          columnName : this.columnName,
          serversideSorting : ! this.isThirdClick,
          isThirdClick : this.isThirdClick,
        });

      }

      private defaultSort(defaultSort: any) {

        this.priviousShorting = `prevValue : ${defaultSort.prevValue}`;
        this.currentShorting = `newValue : ${defaultSort.newValue}`;
        this.columnName = `column name : ${defaultSort.column.name}`;
        this.serversideSorting = true;

        this.sort.emit({
          currentShorting : this.currentShorting,
          priviousShorting : this.priviousShorting,
          columnName : this.columnName,
          serversideSorting :  this.serversideSorting,
          isThirdClick : !this.serversideSorting,
        });
        return defaultSort.sorts[0];
      }

      onRowEditable(row, rowIndex) {
        this.editableRowIndex = rowIndex;
        this.isRowEditable = true;
        this.editingRowKeys[rowIndex] = {...row};
        this.editableRow.emit({
          rowIndex,
          row
        });
      }

      onSaveEdit(row, rowIndex) {
        this.isRowEditable = false;
        this.saveEdit.emit({
          rowIndex,
          row
        });
      }
      onCancelEdit(row, rowIndex) {
        this.dataSource[rowIndex] = this.editingRowKeys[rowIndex];
        this.dataSource = [...this.dataSource];
        this.isRowEditable = false;
        this.cancelEdit.emit({
          rowIndex,
          row
        });
      }

      getRowClass = (row) => {
        // Data set should always have hidden / visible id column
        const editableRowClass = (this.isRowEditable && row.id === this.editingRowKeys[this.editableRowIndex].id)
          ? 'editable-row'
          : 'normal-row';
        return editableRowClass;
      }
    }
<h3>Data table pinned</h3>
    <app-ngx-data-table
      [dataSource]="dataSet"
      [columns]="columns"
      [columnMode]="'force'"
      [isFooterAllowed]="true"
      [rowsPerPage]="10"
      [selected]="selectedRows"
      [selectionType]="selectionType.checkbox"
      [displayCheckbox]="displayCheck"
      [isRightClick]="true"
      (select)="onSelect($event)">
    </app-ngx-data-table>
import { Component, OnInit} from '@angular/core';
    import {SelectionType, TableColumn} from '@swimlane/ngx-datatable';
    import {DataTableService} from '@app/shared/services/data-table.service';

    @Component({
      selector: 'app-data-table-pinned',
      templateUrl: './data-table-pinned.component.html',
      styleUrls: ['./data-table-pinned.component.scss']
    })
    export class DataTablePinnedComponent implements OnInit {

      dataSet: any = [];
      columns: TableColumn[];

      selectedRows: any[] = [];
      selectionType = SelectionType;

      constructor(private tableDataService: DataTableService) { }

      ngOnInit(): void {
        this.fetchData();
        this.setColumns();
      }

      fetchData() {
        this.tableDataService.getTableData()
          .then(response => {
            this.dataSet = response;
          });
      }

      setColumns() {
        this.columns = [
          {prop: 'name', name: 'Name'},
          {prop: 'age', name: 'Age'},
          {prop: 'gender', name: 'Gender'},
          {prop: 'company', name: 'Company Name'}];
      }

      onSelect(event: any) {
        console.log('event: ', event);
        console.log('Selected Row(s) : ', this.selectedRows);
      }

      onActivate(event: any) {
        console.log('event: ', event);
        console.log('Selected Row(s) : ', this.selectedRows);
      }

      displayCheck(row) {
        return row.name !== 'Ethel Price';
      }
    }

pxa4L 02 03 04 05

noumanhussain60470 commented 2 years ago

Facing the same issue, any clue ????