Wijmo5 flexgrid with Angular8 - checkbox, dropdown dependency in Edit mode

Posted by: madhuchhanda_kar on 20 April 2020, 12:17 pm EST

    • Post Options:
    • Link

    Posted 20 April 2020, 12:17 pm EST

    I am using wijmo 5 flexgrid with Angular 8 and trying to create an inline editable grid with Edit button at every row. Each row has a checkbox column(isWorking) and a dropdown column (Asset Type) which looks like a plain grid cell when Edit button is not clicked. The checkbox should be editable when the Edit button of that specific row is clicked. The dropdown field should be editable when the Edit button is clicked and the checkbox is checked. If the checkbox is unchecked, the dropdown field reverts to the readonly grid cell and the value should show “–Select Asset Type–”. If again checked, existing Asset Type value, if any, should be selected. The user also can change Asset Type value by changing selection.

    The issues I am facing :

    1. When in Edit mode, the checkbox is added, it always shows checked in the UI, irrespective of the actual value (true/false). How to display the correct checked value in the checkbox?
    2. When in Edit mode, the checkbox is clicked to check/uncheck, how to toggle the Asset Type field between editable dropdown and readonly grid cell, and change the value accordingly?

    The Code is in the link

    https://stackblitz.com/edit/angular-zx3qvc

    app.component.ts

    
    import { Component, ViewChild } from '@angular/core';
    import { CollectionView, ObservableArray } from 'wijmo/wijmo';
    import * as wjCore from 'wijmo/wijmo';
    import * as wjInput from 'wijmo/wijmo.input';
    import * as wjGrid from 'wijmo/wijmo.grid';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      private _currentEditItem: any = null;
      
      public data : any;
      public deptList: any = [
        {'deptId':'D1', 'deptName':'Accounts'},
        {'deptId':'D2', 'deptName':'Development'},
        {'deptId':'D3', 'deptName':'HR'}
        ];
      public assetTypeList: any = [
        {'assetTypeId':'', 'assetTypeName':'-- Select Asset Type --'},
        {'assetTypeId':'A1', 'assetTypeName':'Desktop'},
        {'assetTypeId':'A2', 'assetTypeName':'Laptop'}
        ];
      public empList: any = [
        {'empId':'E1', 'empName':'AA', 'deptId':'D3', 'isWorking':true, 'assetTypeId':'A2'},
        {'empId':'E2', 'empName':'BB', 'deptId':'D2', 'isWorking':false, 'assetTypeId':''},
        {'empId':'E3', 'empName':'CC', 'deptId':'D1', 'isWorking':false, 'assetTypeId':''},
        {'empId':'E4', 'empName':'DD', 'deptId':'D2', 'isWorking':true, 'assetTypeId':'A1'}
      ];
      
      @ViewChild('flex') flex:wjGrid.FlexGrid;
    
    // we can still use DataMap to display different key and value while in read mode
      public deptMap = new wjGrid.DataMap(this.deptList, 'deptId', 'deptName');
      public assetTypeMap = new wjGrid.DataMap(this.assetTypeList, 'assetTypeId', 'assetTypeName');
    
        constructor() {
          this.data = new wjCore.CollectionView(this.empList);
      } 
      
    initializeGrid(flex: wjGrid.FlexGrid) {
        flex.rows.defaultSize = 40;
        // custom formatter to paint buttons and editors
        flex.formatItem.addHandler((s: wjGrid.FlexGrid, e: wjGrid.FormatItemEventArgs) => {
          if (e.panel == s.cells) {
            let col = s.columns[e.col],
              item = s.rows[e.row].dataItem;
            if (item == this._currentEditItem) {
              // create editors and buttons for the item being edited
              switch (col.binding) {
                case 'buttons':            
                  e.cell.innerHTML = document.getElementById('tplBtnEditMode').innerHTML;
                  e.cell['dataItem'] = item;
                  break;            
                case 'empName':
                  e.cell.innerHTML = '<input class="form-control" ' +
                    'id="' + col.binding + '" ' +
                    'value="' + s.getCellData(e.row, e.col, true) + '"/>';
                  break;
                case 'deptId':
                // create a ComboBox
                  e.cell.innerHTML = '';
                  var cb = new wjInput.ComboBox(wjCore.createElement('<div></div>', e.cell), {
                    itemsSource: this.deptList,
                    displayMemberPath: 'deptName',
                    selectedValuePath: 'deptId'
                  });
                  var dt = e.panel.getCellData(e.row, e.col, false);
                  cb.selectedValue = dt;
                  cb.inputElement.id = col.binding;
                  break;
                case 'assetTypeId':
                // if isWorking is true, create a ComboBox
                  if (item.isWorking){
                    e.cell.innerHTML = '';
                    var cb = new wjInput.ComboBox(wjCore.createElement('<div></div>', e.cell), {
                      itemsSource: this.assetTypeList,
                      displayMemberPath: 'assetTypeName',
                      selectedValuePath: 'assetTypeId'
                    });
                    var dt = e.panel.getCellData(e.row, e.col, false);
                    cb.selectedValue = dt;
                    cb.inputElement.id = col.binding;
                    }
                  break;
                case 'isWorking':            
                  e.cell.innerHTML = '<input type="checkbox" class="wj-cell-check" ' +
                    'id="' + col.binding + '" ' +
                    // 'value="' + s.getCellData(e.row, e.col, true) + '"/>';
                    'value=' + col.collectionView.currentItem.isWorking + ' ' +
                    // 'binding=' + col.collectionView.currentItem.isWorking + ' ' +
                    'checked=' + col.collectionView.currentItem.isWorking + ' />';
                  const chk = <HTMLInputElement>e.cell.firstChild;
                  chk.addEventListener('click', () => {
                    const row = flex.rows[e.row];
                    //console.log(row);
                    if (chk.checked)
                      console.log('yes');
                    else
                      console.log('no');
                  })
                    
                  break;
              }
            } else {
              // create buttons for items not being edited
              switch (col.binding) {
                case 'buttons':
                  e.cell.innerHTML = document.getElementById('tplBtnViewMode').innerHTML;
                  e.cell['dataItem'] = item;
                  break;
              }
            }
          }
        });
    
        // handle button clicks
        flex.addEventListener(flex.hostElement, 'click', (e: MouseEvent) => {
          let targetBtn: HTMLButtonElement;
          if (e.target instanceof HTMLButtonElement) {
            targetBtn = e.target;
          }
          // else if (e.target instanceof HTMLSpanElement && e.target.classList.contains('glyphicon')) {
          //   targetBtn = e.target.parentElement as HTMLButtonElement;
          // }
          if (targetBtn) {
            // get button's data item
            let item = wjCore.closest(targetBtn, '.wj-cell')['dataItem'];
            // handle buttons
            switch (targetBtn.id) {
              // start editing this item
              case 'btnEdit':
                this._editItem(item);
                break;
              // remove this item from the collection
              // case 'btnDelete':
              //   (<wjCore.CollectionView>flex.collectionView).remove(item);
              //   break;
              // commit edits
              case 'btnOK':
                this._commitEdit();
                break;
              // cancel edits
              case 'btnCancel':
                this._cancelEdit();
                break;
            }
          }
          e.preventDefault();
        });
    
        // exit edit mode when scrolling the grid or losing focus
        flex.scrollPositionChanged.addHandler(this._cancelEdit.bind(this));
        flex.lostFocus.addHandler(this._cancelEdit.bind(this));
      }
    
      private _editItem(item: any) {
        this._cancelEdit();
        this._currentEditItem = item;
        this.flex.invalidate();
      }
    
      private _commitEdit() {
        if (this._currentEditItem) {
          this.flex.columns.forEach((col: any) => {
            let input = <HTMLInputElement>this.flex.hostElement.querySelector('#' + col.binding);
            if (input) {
              let value = wjCore.changeType(input.value, col.dataType, col.format);
              // get the key value to update in CollectionView
              if(col.binding === 'deptId') {
                value = this.deptMap.getKeyValue(value);
              }
              if (col.binding === 'isWorking') {
                value = wjCore.changeType(input.checked, col.dataType, col.format);
              }
              if (wjCore.getType(value) == col.dataType) {
                this._currentEditItem[col.binding] = value;
              }
            }
          });
        }
    
        console.log(this._currentEditItem);
        this._currentEditItem = null;
        this.flex.invalidate();
      }
    
      private _cancelEdit() {
        if (this._currentEditItem) {
          this._currentEditItem = null;
          this.flex.invalidate();
        }
      }
    
    }
    
    
    

    app.component.html

    
    
    <div class="header">
    	<div class="container">
    		<h1>
    			
    		</h1>
    	</div>
    </div>
    
    <!-- content -->
    <div class="container">
    	<div>
    		<wj-flex-grid #flex [itemsSource]="data"
    			[headersVisibility]="'Column'" (initialized)="initializeGrid(flex)">
    			<wj-flex-grid-column [header]="'Employee Name'" [binding]="'empName'" [isReadOnly]="true" [width]="'4*'">
    			</wj-flex-grid-column>
    			<wj-flex-grid-column [header]="'Department'" [binding]="'deptId'" [isReadOnly]="true" [width]="'4*'" [dataMap]="deptMap">
    			</wj-flex-grid-column>
          <wj-flex-grid-column [header]="'isWorking'" [binding]="'isWorking'" [isReadOnly]="true" [width]="'4*'">
    			</wj-flex-grid-column>
          <wj-flex-grid-column [header]="'Asset Type'" [binding]="'assetTypeId'" [isReadOnly]="true" [width]="'4*'" [dataMap]="assetTypeMap">
    			</wj-flex-grid-column>
    			<wj-flex-grid-column [header]="'Actions'" [binding]="'buttons'" [isReadOnly]="true" [width]="'3*'"></wj-flex-grid-column>
    		</wj-flex-grid>
    
    		<!-- template for buttons on items in view mode -->
    		<div id="tplBtnViewMode" style="display:none">
    			<button id="btnEdit" class="btn btn-default btn-sm">
            Edit
        </button>
    		</div>
    
    		<!-- template for buttons on items in edit mode -->
    		<div id="tplBtnEditMode" style="display:none">
    			<button id="btnOK" class="btn btn-primary btn-sm">
            OK
        </button>
    			<button id="btnCancel" class="btn btn-warning btn-sm">
            Cancel
        </button>
    		</div>
    	</div>
    </div>
    
    
  • Posted 21 April 2020, 2:14 am EST

    Hi Madhu,

    Regarding your first issue, you have set the checked property of the checkbox. It does not matter whether this property is true or false, if the checked attribute is present in the HTML, it will always be checked. So, instead of setting the checked attribute in HTML, simply set the checked property at JS side by getting the reference of the checkbox:

    
    case 'isWorking':
    	.....
            const chk = <HTMLInputElement>e.cell.firstChild;
            chk.checked = col.collectionView.currentItem.isWorking;
    
    

    Regarding your 2nd issue, I would suggest you create the ComboBox regardless of whether isWorking is true or false. But, you can disable the ComboBox by using the isDisabled. Set it according to the isWorking property and in the click event of the checkbox, enable/disable the ComboBox accordingly:

    
    case 'assetTypeId':
                // if isWorking is true, create a ComboBox
                  e.cell.innerHTML = '';
                    var cb = new wjInput.ComboBox(wjCore.createElement('<div id="assetType"></div>', e.cell), {
                      itemsSource: this.assetTypeList,
                      displayMemberPath: 'assetTypeName',
                      selectedValuePath: 'assetTypeId'
                    });
                    var dt = e.panel.getCellData(e.row, e.col, false);
                    cb.selectedValue = dt;
                    cb.inputElement.id = col.binding;
                    cb.isDisabled = !col.collectionView.currentItem.isWorking;
                  break;
    
    chk.addEventListener('click', (e) => {
                    var assetTypeCB = wjCore.Control.getControl('#assetType') as wjInput.ComboBox;
                    console.log(chk.checked)
                    assetTypeCB.isDisabled = chk.checked;
    })
    
    

    Refer to the updated sample below:

    https://stackblitz.com/edit/angular-ue6nvy

    Regards,

    Ashwin

  • Posted 24 April 2020, 11:44 am EST

    Thanks for your prompt reply. The code provided by you in link :

    https://stackblitz.com/edit/angular-ue6nvy

    is having an weird issue.

    To replicate the issue, please follow below steps:

    1. In the first row with Employee Name ‘AA’, click on Edit, uncheck IsWorking checkbox, click OK.
    2. In the second row with Employee Name ‘BB’, click on Edit, check IsWorking checkbox, click OK. It reverts to unchecked. Trying this step several times will all result in unchecked checkbox. Same is the case for the rest of the rows.

    Could you check?

  • Posted 27 April 2020, 2:33 am EST

    Hi,

    The reason for this issue is that all the editing elements created are removed once editing is completed except for the checkbox. So, when the isWorking of AA row is set to false, the editing checkbox remains in the DOM. And when any other row is edited, the commitEdit method gets the reference of the first checkbox, which is of AA row, and since it is not selected, the value is changed to false.

    To solve this issue, you can add the row’s index with the id of the checkbox so you can get the current row’s checkbox easily.

    Refer to the updated sample below:

    https://stackblitz.com/edit/angular-1jszmv

    ~regards

  • Posted 7 May 2020, 5:34 am EST

    Thanks Aswin, its working perfectly now.

Need extra support?

Upgrade your support plan and get personal unlimited phone support with our customer engagement team

Learn More

Forum Channels