Our blazing fast Grid component built with pure JavaScript


Post by anuradha.gopal »

Hi,
I have a custom menu handler to add records inside the grid with the Insert Before and Insert After options. This code works fine in the regular scenario

In the current requirement, I have a Bryntum grid inside a div element. I also have an Add button that will enable me to add another instance of the div along with the grid. The Add button should allow me to add any number of grid instances this way.
The issue I am facing in this case is when I am adding the grid dynamically, I am able to add records for the current grid. However, when I add the 2nd grid and go back to the 1st grid to add any row before or after a selected row, the new record is added to the 2nd grid (the latest grid) and not the 1st.
(If there are 5 grids, and you try to add a record to any grid created prior to it, the record gets added in the 5th grid)

It would be great if you can provide a working snippet that will guide me in this scenario.

 features: {
        sort: false,
        cellEdit: true,
        rowCopyPaste : false,
        cellMenu: {
            items: {
              insertBefore: {
                text: 'Insert above',
                icon: 'b-fa b-fa-plus',
                cls: 'b-separator color',
                weight: 110, // Insert after the first default item
                onItem: ({ item, record, column }) => {
                  const selected = grid.selectedRecords;
                  var index = grid.store.indexOf(selected[0])

              var added = grid.store.insert(index,
                {
                      "intervalSequenceNumber": null,
                      "perfOperationID": null,
                      "intervalID": null,
                     //...
                }
              );
              grid.selectedRecord = added[0];
              setSequenceNumber();
            }
          },
          insertAfter: {
            text: 'Insert below',
            icon: 'b-fa b-fa-plus',
            cls: 'b-separator color',
            weight: 110, // Insert after the first default item
            onItem: ({ item, record, column }) => {
              const selected = grid.selectedRecords;
              var index = grid.store.indexOf(selected[0])

              var added = grid.store.insert(index + 1,
                {
                  "intervalSequenceNumber": null,
                  "perfOperationID": null,
                  "intervalID": null,
                  //...
                }
              );
              grid.selectedRecord = added[0];
              setSequenceNumber();
            }
          },

        }
      },
},

Please note that I am adding records using a context menu for Add.

addEmptyRecords(gridObjTemp) {
        const menu = new Menu({
          anchor: true,
          autoShow: false,
          items: [
            {
              icon: 'b-fw-icon b-icon-add',
              text: 'Add'
            },
          ],

      onItem(item) {
        var added = gridObjTemp.store.add(
          {
            "intervalSequenceNumber": null,
            "perfOperationID":null,
            "intervalID":null,
            //....
          }
        );
        gridObjTemp.selectedRecord = added[0];
      }
    });

      if(gridObjTemp.subGrids){
        gridObjTemp.subGrids.normal.currentElement.addEventListener("contextmenu", (ev) => {
          if (gridObjTemp.store.records.length === 0) {
            menu.show();
            menu.setXY(ev.clientX, ev.clientY);
            ev.preventDefault();
          }
        })
      }
}

Post by marcio »

Hey anuradha.gopal,

Could you provide a runnable test case for your scenario? I didn't see in the snippets the way that you're adding the new divs with the Grid elements. Also, are you using the same store for all the Grids or you are creating new ones with them?

Best regards,
Márcio


Post by anuradha.gopal »

Hi Marcio,
It is difficult to provide a working example, but will try. Is it possible from your end to provide an example?
I am using the same store in this case.


Post by alex.l »

Hi,

  1. Make sure your variable gridObjTemp in the handler refer to correct grid instance. Maybe you rewrite value with the last created instance. add debugger into the method and check that.
  2. If you want to share store and have same dataset for all grid instances, you need to chain from original store and not use same store instance.
    https://bryntum.com/products/grid/docs/api/Core/data/Store#function-makeChained

Is it possible from your end to provide an example?

I didn't get this one. Example of your application? We have a bunch of demo apps in /examples folder that you can use as a base and apply required changes to make this problem reproducible. We need to run your code and debug to help you with that. Visually I see only one possible problem - wrong gridObjTemp link. Very hard to say more without debugging.

All the best,
Alex


Post by anuradha.gopal »

Hi Alex,
I too suspect it is the instance, not sure what I am missing. Please check the attached code.
Steps to replicate:

  1. Add 1st grid and add records to it via the context menu.
  2. Add 2nd grid and add records to the 2nd grid.
  3. Now go back to the 1st grid and use the "Insert above" or "Insert below" context menu.

A row gets added in the 2nd grid and not the 1st.

Vue file:

<template>
  <div>
    <div>
      <div v-for="(data, index) in sampleList" :key="index" class="col-lg-12 ">
        <span style="font-weight: bold; padding-top: 10px; padding-left: 0px; padding-bottom: 10px;"
                        class="col-lg-1 pr-2">Test {{ data.sNo }}</span>
                      <span style="font-weight: bold">:</span>
                      <span style="float: right"><button @click="onClickDelete(index)" class="button-edit"><i
                          style=" font-size: 12px;" class="fas fa-trash"></i></button>
                      </span>

          <bryntum-grid ref="sampleGridRef" v-bind="SampleGrid" />
  </div>
</div>

<div >
  <button @click="addRecord()" class="button-edit mt-4 ">Add <i class="fas fa-plus"></i></button>      
</div>

  </div>

</template>
<script>
import { BryntumGrid } from "@bryntum/grid-vue";
import {Menu, Toast } from '@bryntum/grid';
import { SampleGrid } from "./SampleGrid";
export default {
  name: "SampleTest",
  components: {
    BryntumGrid
  },
  data() {
    return {
      SampleGrid,
      sampleGridObj: null,
      sampleGridDS: [],
      sampleList: [
        /* {
          sNo: '1'
        }, 
        {
          sNo: '2'
        }, 
        {
          sNo: '3'
        }, */
      ]
    }
  },
  methods: {
    
addRecord: function() { let obj = { sNo: this.sampleList.length + 1 } this.sampleList.push(obj) let that=this; this.$nextTick(() => { that.sampleGridObj = that.$refs.sampleGridRef; const gridListCount = that.sampleGridObj.length-1; const gridInstance=that.sampleGridObj[gridListCount].instance; gridInstance.rowHeight = 25; that.addEmptyRecords(gridInstance); }); }, onClickDelete: function(index) { this.sampleList.splice(index, 1) }, initializeGrid: function(){ if(this.$refs.sampleGridRef){ let sampleGridObj = this.$refs.sampleGridRef; sampleGridObj.forEach(element => { const gridInstance=element.instance; gridInstance.rowHeight = 25; this.addEmptyRecords(gridInstance); }); } }, addEmptyRecords(gridObjTemp) { const menu = new Menu({ anchor: true, autoShow: false, items: [ { icon: 'b-fw-icon b-icon-add', text: 'Add' }, ], onItem(item) { var added = gridObjTemp.store.add( { "intervalSequenceNo": null, "Col1": null, "Col2": null, "Col3": null, "action": null } ); gridObjTemp.selectedRecord = added[0]; } }); if(gridObjTemp.subGrids){ gridObjTemp.subGrids.normal.currentElement.addEventListener("contextmenu", (ev) => { if (gridObjTemp.store.records.length === 0) { menu.show(); menu.setXY(ev.clientX, ev.clientY); ev.preventDefault(); } }) } }, }, async mounted() { this.initializeGrid() }, } </script> <style lang="scss"> @import "../../Specification/BryntumScss.scss"; .shadow-box-section { box-shadow: rgb(0 0 0 / 25%) -2px -1px 6px; border-radius: 5px; } .header-content { cursor: pointer; display: flex; align-items: center; } </style>

JS file

var grid;
const SampleGrid = {
    selectionMode: { multiSelect : false },
    stateId: 'docs-childgrid-state',
    features: {
        sort: false,
        cellEdit: true,
        rowCopyPaste : false,
        cellMenu: {
            items: {
              insertBefore: {
                text: 'Insert above',
                icon: 'b-fa b-fa-plus',
                cls: 'b-separator color',
                weight: 110, // Insert after the first default item
                onItem: ({ item, record, column }) => {
                  const selected = grid.selectedRecords;
                  var index = grid.store.indexOf(selected[0])

              var added = grid.store.insert(index,
                {
                  "intervalSequenceNo": null,
                  "Col1": null,
                  "Col2": null,
                  "Col3": null,
                  "action": null
                }
              );
              grid.selectedRecord = added[0];
              setSequenceNumber();
            }
          },
          insertAfter: {
            text: 'Insert below',
            icon: 'b-fa b-fa-plus',
            cls: 'b-separator color',
            weight: 110, // Insert after the first default item
            onItem: ({ item, record, column }) => {
              const selected = grid.selectedRecords;
              var index = grid.store.indexOf(selected[0])

              var added = grid.store.insert(index + 1,
                {
                  "intervalSequenceNo": null,
                  "Col1": null,
                  "Col2": null,
                  "Col3": null,
                  "action": null
                }
              );
              grid.selectedRecord = added[0];
              setSequenceNumber();
            }
          },

        }
      },
},
showDirty: true,
listeners: {
  renderRows: function ({ source }) {
      grid = source;
  },

  },
    height: 280,
    columns: {
        data: [
              {
                text: '', align: 'center',
                field: 'intervalSequenceNo',
                hidden: true
            },
            {
              text: 'Col1', align: 'center',
              field: 'Col1',
              hidden: false
            },
            {
              text: 'Col2', align: 'center',
              field: 'Col2',
              hidden: false
            },
            {
              text: 'Col3', align: 'center',
              field: 'Col3',
              hidden: false
            },
            //...
            
], }, store: { listeners: { remove(){ setSequenceNumber(); } } }, }; function setSequenceNumber() { var childrecord = grid.store.records for (let i = 0; i < childrecord.length; i++) { grid.store.getAt(i).intervalSequenceNo =i+1; } } export { SampleGrid };

Post by alex.l »

Hard to say visually, we need to launch the app and debug it.
Please zip and attach full runnable application with instructions how to build and run it and clear steps to reproduce.
If you don't want to share your app, use one of our examples as a base, apply minimal changes to make it reproducible and share with us.
Here is the guide how to ask for help viewtopic.php?t=772

All the best,
Alex


Post by anuradha.gopal »

I have attached the source code of the "full runnable application" . Also attached screenshots.

I believe my steps given earlier are simple and clear enough to reproduce. The screenshots should make things more clear.

Attachments
vue-renderer.zip
Runnable application
(238.89 KiB) Downloaded 26 times
Screenshots.zip
Screenshots to reproduce the issue
(159.02 KiB) Downloaded 20 times

Post by marcio »

Hey anuradha.gopal,

I believe you attached a demo example, this one https://www.bryntum.com/products/grid/examples/frameworks/vue/javascript/vue-renderer/dist/

Alex meant that you could get the source from this demo as an example and then add the snippets that you shared here viewtopic.php?p=124704#p124704 to and share with us to check.

Best regards,
Márcio


Post by anuradha.gopal »

OK, I had attached the wrong folder. Reattaching. again. This is the modified source of your example for replication.

Attachments
vue-renderer.zip
Runnable application
(382.41 KiB) Downloaded 25 times

Post by alex.l »

Thanks for the test case. As we assumed, I added debugger into your insertBefore menu item handler and found that grid variable you used in there contains last created grid instance. This happened because you use gridConfig many times with one global grid variable which was rewriting on every renderRows call, which happens at least once when new grid instance is rendered.
To fix this problem, you should define variable that will not be rewrote and use it. Or use source in param list of the onItem method.

                onItem: ({ item, record, column, source : grid }) => {
                  const selected = grid.selectedRecords;
                  var index = grid.store.indexOf(selected[0])

              var added = grid.store.insert(index,
                {
                  "intervalSequenceNo": null,
                  "Col1": null,
                  "Col2": null,
                  "Col3": null,
                  "action": null
                }
              );
              grid.selectedRecord = added[0];
              setSequenceNumber();
            }

All the best,
Alex


Post Reply