Our blazing fast Grid component built with pure JavaScript


Post by Exigo »

Hello,

I have recently been trying to implement a grid that has items that has an object reference to another custom store in the project.

I am looking for any idea as to how and why the search functionality disappears from the combo widget whenever I use store instead of items in the combo.

The below code shows the attached country perfectly and sets the country correctly,

// SEARCH / FILTER ON COLUMN COMBO DOES NOT WORK IN THIS CODE
export const SUPPLIER_GRID_COLUMNS = (project: any) => {
  const countryCombo = new Combo({
    label: 'country',
    store: project.countryStore,
    displayField: 'name'
  });

  return [
    {
      id: 'country',
      field: 'countryName',
      text: 'Country',
      editor: countryCombo,
      flex: 1
    },
  ]
}

however the search/filter stops working when compared to this, which is the same setup except the store is referenced not from the project, but in essence the stores are the same

// SEARCH / FILTER ON COLUMN COMBO WORKS IN THIS CODE
const countryStore = new AjaxStore({
  fields: ['id', 'name'],
  data: countries,
  autoLoad: true
});

export const SUPPLIER_GRID_COLUMNS = (project: any) => {
  const countryCombo = new Combo({
    label: 'country',
    store: countryStore ,
    displayField: 'name'
  });

  return [
    {
      id: 'country',
      field: 'countryName',
      text: 'Country',
      editor: countryCombo,
      flex: 1
    }
  ]
}

I would very much like to keep the store inside the project since it has more functionality when working with schedulerPro logic.
So do you have any idea where to look to try to resolve my search/filter issues?


Post by Animal »

Debug it:

Screenshot 2023-05-17 at 17.33.16.png
Screenshot 2023-05-17 at 17.33.16.png (88.76 KiB) Viewed 462 times

Post by Exigo »

Sorry for the late response.. At that point in time there is no records in countryStore, but the store is created correctly when project is created. But the grid is then created before project is done loading data from the backend.
However even if I try to not start the grid until the countryStore is populated with a list of the countries, it still does not let me search in the combo box. But the data is showing correctly still.

Otherwise if you can try to help me to what I am looking for with "what is in countryStore" maybe I can be more of help?


Post by alex.l »

All the best,
Alex


Post by Exigo »

I did set displayField?, but I didn't really want a valueField, as I just wanted itself (the object) to be the value
But I tried to add the valuefield of id, but it changed nothing

combo_with_valuefield.png
combo_with_valuefield.png (7.27 KiB) Viewed 382 times

data is pretty simple, looks like this after entering the store:

data_example.png
data_example.png (47.26 KiB) Viewed 382 times

But again, everything seems to work fine, except that I cannot search in the combo.
So setting the reference and everything is fine


Post by alex.l »

It works well on our side.

Here is fully runnable code that you can copy and paste into any of our examples and test

let newPlayerCount = 0;

const countryStore = new AjaxStore({
    fields: ['id', 'name']
  });

  window.loadDataToStore = () => {
    countryStore.loadData([{
        id: 1,
        name : 'Germany'
    }, {
        id : 2,
        name : 'Sweden'
    }, {
        id : 3,
        name : 'Poland'
    }])
  }

setTimeout(loadDataToStore, 2000);

const grid = new Grid({

appendTo : 'container',

features : {
    cellEdit : true,
    sort     : 'name',
    stripe   : true
},

// Show changed cells
showDirty : true,

async validateStartDateEdit({ grid, value }) {
    if (value > DateHelper.clearTime(new Date())) {
        return grid.features.cellEdit.confirm({
            title   : 'Selected date in future',
            message : 'Update field?'
        });
    }
    return true;
},

columns : [
    { text : 'Name', field : 'name', flex : 1 },
    {
        text   : 'Birthplace',
        field  : 'city',
        width  : '8em',
        editor : { type : 'dropdown', items : DataGenerator.cities }
    },
    {
        text   : 'Country',
        field  : 'country',
        width  : '8em',
        editor : { type : 'dropdown', store : countryStore, valueField: 'id', displayField : 'name' }
    },
    { text : 'Team', field : 'team', flex : 1 },
    { text : 'Score', field : 'score', editor : 'number', width : '5em' },
    {
        text             : 'Start',
        id               : 'start',
        type             : 'date',
        field            : 'start',
        width            : '9em',
        finalizeCellEdit : 'up.validateStartDateEdit'
    },
    { text : 'Finish (readonly)', type : 'date', field : 'finish', width : '9em', editor : false },
    { text : 'Time', id : 'time', type : 'time', field : 'time', width : '10em' },
    // Column using the custom widget defined above as its editor
    {
        text     : 'Custom', // `text` gets localized automatically, is added to examples/_shared/locales/*
        field    : 'done',
        editor   : 'yesno',
        width    : '5em',
        renderer : ({ value }) => value ? YesNo.L('L{Object.Yes}') : YesNo.L('L{Object.No}')
    },
    { type : 'percent', text : 'Percent', field : 'percent', flex : 1 }
],

data : DataGenerator.generateData(50),

listeners : {
    selectionChange({ selection }) {
        removeButton.disabled = !selection.length || grid.readOnly;
    }
},

tbar : [
    {
        type        : 'button',
        ref         : 'readOnlyButton',
        text        : 'Read-only',
        tooltip     : 'Toggles read-only mode on grid',
        toggleable  : true,
        icon        : 'b-fa-square',
        pressedIcon : 'b-fa-check-square',
        onToggle    : ({ pressed }) => {
            addButton.disabled = insertButton.disabled = grid.readOnly = pressed;

            removeButton.disabled = pressed || !grid.selectedRecords.length;
        }
    },
    {
        type  : 'buttongroup',
        items : [
            {
                type     : 'button',
                ref      : 'addButton',
                icon     : 'b-fa-plus-circle',
                text     : 'Add',
                tooltip  : 'Adds a new row (at bottom)',
                onAction : () => {
                    const
                        counter = ++newPlayerCount,
                        added   = grid.store.add({
                            name : `New player ${counter}`,
                            cls  : `new_player_${counter}`
                        });

                    grid.selectedRecord = added[0];
                }
            },
            {
                type     : 'button',
                ref      : 'insertButton',
                icon     : 'b-fa-plus-square',
                text     : 'Insert',
                tooltip  : 'Inserts a new row (at top)',
                onAction : () => {
                    const
                        counter = ++newPlayerCount,
                        added   = grid.store.insert(0, {
                            name : `New player ${counter}`,
                            cls  : `new_player_${counter}`
                        });

                    grid.selectedRecord = added[0];
                }
            }
        ]
    },
    {
        type     : 'button',
        ref      : 'removeButton',
        color    : 'b-red',
        icon     : 'b-fa b-fa-trash',
        text     : 'Remove',
        tooltip  : 'Removes selected record(s)',
        disabled : true,
        onAction : () => {
            const selected = grid.selectedRecords;

            if (selected && selected.length) {
                const
                    store      = grid.store,
                    nextRecord = store.getNext(selected[selected.length - 1]),
                    prevRecord = store.getPrev(selected[0]);

                store.remove(selected);
                grid.selectedRecord = nextRecord || prevRecord;
            }
        }
    }
]
});

const { addButton, removeButton, insertButton } = grid.widgetMap;

// Show the dirty marker
grid.store.getAt(0).score = 200;

It uses editor with AjaxStore and data loaded into the store with 2sec delay.
I see it fully working.
Please apply required changes to make the bug reproducible or attach runnable test case that we can debug.

Here is video

Screen Recording 2023-06-02 at 11.55.57.mov
(5.25 MiB) Downloaded 22 times

All the best,
Alex


Post Reply