Our blazing fast Grid component built with pure JavaScript


Post by rodel.ocfemia »

Hi,

I tried setting up Grid state for localStorage but the expand/collapse state of each record are not saved. What is the best way to save the state of expand/collapse of each row?

StateProvider.setup('local');

Thanks


Post by marcio »

Best regards,
Márcio


Post by rodel.ocfemia »

Hi marcio,

I have the following code. The StateProvider.saveState() does not return any error. But how to load the saved stated on load of grid? I tried invoking StateProvider.loadState('mainGrid',true) inside renderRows event but it throws error.

ProjectGrid = () => {
    const gridConfig = {
      stateId: 'mainGrid',
      store: new AjaxStore({
        readUrl: 'http://localhost/project/projectgrid/data',
        autoLoad: true,
      }),
      treeFeature: true,
      columns: [
        {
          type: 'tree',
          text: '',
          field: 'name',
          width: 200,
          readonly: true,
        },
      ],
      listeners: {
        beforeCollapse: (props: any) => {
          StateProvider.saveState();
        },
        beforeExpand: (props: any) => {
          StateProvider.saveState();
        },
        renderRows:  (props: any) => {
           StateProvider.loadState('mainGrid',true); //this throws error
        }
    };
    const grid = useRef<any>(null);
    return <BryntumGrid ref={grid} {...gridConfig} />;
  };
}

Post by marcio »

Hey,

Did you initiate the StateProvider correctly? Check this demo to see how it's done https://bryntum.com/products/grid/examples/state/

It uses the setup function you shared in the first post.

if ((new URL(window.location)).searchParams.get('state') === 'remote') {  // if ('?state=remote' in URL)
    // For server-side state, initialize the page widgets after the state is loaded from the server.
    (new BackendState(StateProvider.setup('memory'))).init().then(launch);
}
else {
    // To use localStorage instead of a backend for state, the launch process is simply this:
    StateProvider.setup('local');
    launch();
}

Best regards,
Márcio


Post by rodel.ocfemia »

Hi Marcio,

Yes I initiated this code StateProvider.setup('local').
Please see attached runnable code.

grid-state-react-typescript.zip
(1.7 MiB) Downloaded 48 times

I've added also the following code to save the expand/collapse state.

listeners: {
            collapseNode: (props: any) => {
                const { source } = props;
                source.saveState();
                console.log('collapseNode')
            },
            expandNode: (props: any) => {
                const { source } = props;
                source.saveState();
                console.log('expandNode')
            },
        },

Post by alex.l »

Hi,

I assume there is some misunderstanding here.
expand/collapse state of records is not stored in state provider. It is a data field of a record and should be stored in db on server side.
See https://bryntum.com/products/grid/docs/api/Grid/data/GridRowModel#field-expanded
So, you need to make this field persist: true and save it as any other data field change.
https://bryntum.com/products/grid/docs/api/Core/data/field/DataField#config-persist
https://bryntum.com/products/grid/docs/api/Core/data/AjaxStore#config-modelClass

class MyModel extends GridRowModel {
  static get fields() {
      return [
          { name : 'expanded', persist : true }
      ]
  }

  [...]
  
const store = new AjaxStore({ modelClass : MyModel }); });

All the best,
Alex


Post by rodel.ocfemia »

Hi Alex,

Right now, we can only save user preference including expand/collapse in the user's browser. So I created a custom StateHelper class that saves the expand/collapse to localStorage. See attached rerunnable code.

grid-state-react-typescript2.zip
(1.7 MiB) Downloaded 44 times

The saving part works fine as follows.

listeners: {
            collapseNode: (props: any) => {
                const { record, source } = props;
                const data = {
                  id: record.id,
                  expanded: false,
                };
                StateHelper.saveExpandCollapse(data);
            },
            expandNode: (props: any) => {
                const { record, source } = props;
                const data = {
                    id: record.id,
                    expanded: true,
                  };
                  StateHelper.saveExpandCollapse(data);
            },
}

On rendering, I'm invoking it on renderRow. It also works fine. It renders whatever I saved on localStorage.
The issue is that I am unable to trigger the collapse anymore. Please advise how can I fix the issue.

renderRow: (props: any) => {
                const { record, source } = props;
                const expandCollapseState = StateHelper.getExpandCollapse();
                if (expandCollapseState) {
                    const row = expandCollapseState.items.filter((x: any) => x.id === record.id);
                    if (row.length) {
                        source.grid.toggleCollapse(record, !row[0].expanded);
                    }
                }
            },

Post by alex.l »

Hi,

The issue is that I am unable to trigger the collapse anymore. Please advise how can I fix the issue.

Could you please explain what did you mean here? Unable to collapse node programmatically? Unable to catch node collapse action?

I would try in your case to manipulate with data and set expanded field value before you load (apply) data to the store instead of manipulate already loaded and rendered data.

All the best,
Alex


Post by rodel.ocfemia »

Hi Alex,

To recreate the issue:

  1. Run the code I attached in the previous post (grid-state-react-typescript2.zip)
  2. On load, try to expand one row, then refresh the page using Reload button in the browser.
  3. After refresh of the page, the expanded row will remain expanded. This works as expected.
  4. Try to click collapse icon on the expanded row. It will not work anymore. This is the issue.

All the saving and loading of state are placed inside the following code.
Note: If I comment the code inside renderRow, clicking expand or collapse icon works. But on load, the saved state is not reflected. The code in renderRow is the one that applies the expand/collapse on load.

listeners: {
            collapseNode: (props: any) => {
                const { record, source } = props;
                const data = {
                  id: record.id,
                  expanded: false,
                };
                StateHelper.saveExpandCollapse(data);
            },
            expandNode: (props: any) => {
                const { record, source } = props;
                const data = {
                    id: record.id,
                    expanded: true,
                  };
                  StateHelper.saveExpandCollapse(data);
            },
            renderRow: (props: any) => {
                const { record, source } = props;
                const expandCollapseState = StateHelper.getExpandCollapse();
                if (expandCollapseState) {
                    const row = expandCollapseState.items.filter((x: any) => x.id === record.id);
                    if (row.length) {
                        source.grid.toggleCollapse(record, !row[0].expanded);
                    }
                }
            },
        },

Post by alex.l »

Hi, the solution is not valid. You are changing node state while rendering that causes collisions. It won't be working this way.
There are 2 options:

  1. Change data before load to store.
    You need to get state from localStore, change expanded field value for all required records and load/set data to store after that. You can add your code into useGridConfig method above return object.
  2. Change state after render complete.
    You need to subscribe on event that triggers when rendering finished (as example https://bryntum.com/products/grid/docs/api/Core/widget/Widget#event-paint ) and apply state to records after rendering. Of course, in this case nodes will "jump" because original state and applied state will be different.

In 2 words, expanded/collapse state of nodes - is data layer level. So I suggest you to work with it on data level. I am not sure if you used static data in your real app, but in any case there is a way to apply state on data before rendering.

All the best,
Alex


Post Reply