React - FlexGrid restoring persisted state clears all column templates

Posted by: jschnurer on 30 April 2024, 4:01 pm EST

    • Post Options:
    • Link

    Posted 30 April 2024, 4:01 pm EST - Updated 30 April 2024, 4:07 pm EST

    I have a FlexGrid that I render a bunch of columns inside using code like this:

    {gridColumns.map(col =>
      <FlexGridColumn
    	key={col.uid}
    	header={col.headerText}
    	binding={col.field}
    	width={col.width}
    	format={col.format}
    	align={col.textAlign ?? "Left"}
    	visible={defaultBool(col.visible, true)}
    	minWidth={col.minWidth}
    	allowSorting={defaultBool(col.allowSorting, true)}
    	dataType={mapToWijmoDataType(col.type)}
      >
    	{col.template &&
    	  <FlexGridCellTemplate
    		cellType="Cell"
    		template={(context: any) => col.template?.(context.item)}
    	  />
    	}
    
    	{!col.template
    	  && col.field
    	  && col.type === "boolean" &&
    	  <FlexGridCellTemplate
    		cellType="Cell"
    		template={(context: any) =>
    		  Boolean(context.item[col.field!])
    			? "Yes"
    			: "No"
    		}
    	  />
    	}
      </FlexGridColumn>
    )}

    As you can see, if the column is a boolean (and the dev has not specified a template function in the column input), I render a FlexGridCellTemplate and the template is to render “Yes” or “No” in the column.

    This works perfectly.

    However, if I persist the grid and then restore the persisted state, I lose this template and the UI reverts back to the original display for a boolean (a checkbox).

    Here is how I am retrieving and persisting the grid state:

    const gridJson = JSON.stringify({
      columns: gridRef.current.columnLayout,
      filterDefinition: filterObj?.filterDefinition,
      sortDescriptions: gridRef.current.collectionView.sortDescriptions.map(function (sortDesc) {
    	return { property: sortDesc.property, ascending: sortDesc.ascending };
      }),
    });
    
    localStorage[`wijmoGridState_${id}`] = gridJson;

    Then, when it is time to restore the persisted state, I do this:

    var json = localStorage[`wijmoGridState_${id}`];
    
    if (json) {
      var state = JSON.parse(json);
    
      gridRef.current.columnLayout = state.columns;
    
      if (filterObj) {
    	filterObj.filterDefinition = state.filterDefinition;
      }
    
      // Get the grid's current collectionView and restore the sort order info.
      var view = gridRef.current.collectionView;
    
      view.deferUpdate(function () {
    	view.sortDescriptions.clear();
    
    	for (var i = 0; i < state.sortDescriptions.length; i++) {
    	  var sortDesc = state.sortDescriptions[i];
    	  view.sortDescriptions.push(new SortDescription(sortDesc.property, sortDesc.ascending));
    	}
      });
    }

    After restoring, I lose my templates for all columns (even my other, custom ones).

    Help! I want to be able to persist the column widths and their orders and then restore them later. However, I cannot lose the templates!

  • Posted 1 May 2024, 9:06 am EST

    Hi,

    Restoring the cellTemplates with column layout is not officially supported in FlexGrid control, as cellTemplates are not saved with the column layout, hence cell templates are not restored when the layout is restored. So, you’ll need to manually restore the cellTemplates for each column when the layout is restored.

    Please refer to the following sample demonstrating the same - https://jscodemine.mescius.io/share/8kwI9R0gBEimdmV7p3O4Zw/

    Please note that, you may need to use the ‘cellTemplates’ property of the Column to implement it, instead of using ‘FlexGridCellTemplate’ markup, and for the Boolean columns, you’ll need to handle the formatItem event of the flexgrid to modify the cell content as required, as setting the cellTemplates property for columns with Boolean data may not work, because in React the cellTemplates (set using cellTemplates property) are applied first, then checkboxes are rendered, which overrides the cell templates.

    Regards

  • Posted 1 May 2024, 9:19 am EST

    I notice that for assigning a cellTemplate you are returning a string.

    Many of my other cell templates that I use return complex JSX such as buttons or even other custom components. Is that somehow still possible to implement without using the

    <FlexGridCellTemplate />
    ?

  • Posted 1 May 2024, 10:36 am EST

    I was able to come up with an alternative that maintains almost all of the functionality I was seeking.

    Instead over overwriting the gridRef.current.columnLayout during the “restore persisted state” operation, I store the persisted data into a component state variable.

    Then inside the gridColumns.map() where I render out the columns, I use the width and visibility from the state variable holding the persisted state, rather than the width value that comes in from the props. Additionally, I can use the visible property from the persisted values as well.

    So I have this new state variable:

      const [persistedGridColumns, setPersistedGridColumns] = useState<{
        align: string,
        binding: string,
        dataType?: number,
        header: string,
        width?: number,
        visible?: boolean,
      }[] | undefined>(undefined);

    Then, when restoring from localStorage, I do this now:

    var json = localStorage[`wijmoGridState_${id}`];
    if (json) {
      var state = JSON.parse(json);
    
      setPersistedGridColumns(JSON.parse(state.columns).columns);
    
      if (filterObj) {
        filterObj.filterDefinition = state.filterDefinition;
      }
    
      // Get the grid's current collectionView and restore the sort order info.
      var view = gridRef.current.collectionView;
    
      view.deferUpdate(function () {
        view.sortDescriptions.clear();
    
        for (var i = 0; i < state.sortDescriptions.length; i++) {
          var sortDesc = state.sortDescriptions[i];
          view.sortDescriptions.push(new SortDescription(sortDesc.property, sortDesc.ascending));
        }
      });
    }

    Finally, when rendering the grid columns, I do this:

    {gridColumns.map(col => {
      const persistedCol = persistedGridColumns
        ?.find(x => x.header === col.headerText
          && x.binding === col.field);
    
      return (
        <FlexGridColumn
          key={col.uid}
          uid={col.uid}
          header={col.headerText}
          binding={col.field}
          width={persistedCol?.width ?? col.width}
          format={col.format}
          align={col.textAlign ?? "Left"}
          visible={defaultBool(persistedCol?.visible, defaultBool(col.visible, true))}
          minWidth={col.minWidth}
          allowSorting={defaultBool(col.allowSorting, true)}
          dataType={mapToWijmoDataType(col.type)}
        >
          {col.template &&
            <FlexGridCellTemplate
              cellType="Cell"
              template={(context: any) => col.template?.(context.item)}
            />
          }
    
          {!col.template
            && col.field
            && col.type === "boolean" &&
            <FlexGridCellTemplate
              cellType="Cell"
              template={(context: any) =>
                Boolean(context.item[col.field!])
                  ? "Yes"
                  : "No"
              }
            />
          }
        </FlexGridColumn>
      );
    })}

    Notice how the properties width and visible are being set from the persisted state value (if available).

    This seems to be working for for width and visibility but I am unable to figure out a way to restore the column ordering. I tried re-ordering the gridColumns based on the persisted state’s ordering but it did not work since the grid always renders once first before loading the persisted state and then even if I change the ordering the grid will always keep its original ordering.

    Is there a way to restore the column ordering using my above method?

  • Posted 2 May 2024, 6:49 am EST

    Hi,

    Thank you for sharing the new approach with us, it seems to be a good approach to restoring the Column layout along with using the ‘FlexGridCellTemplate’. To maintain the order of the columns you’ll need to update the column ordering after restoring the layout, you can use the ‘moveElement’ method of the columns collection to move the columns to the desired position. Please refer to the following API link for more information about the ‘moveElement’ method - https://developer.mescius.com/wijmo/api/classes/wijmo_grid.columncollection.html#moveelement

    Please refer to the following sample demonstrating the same - https://stackblitz.com/edit/stackblitz-starters-pffl8o?file=src%2FApp.tsx

    Regards

  • Posted 2 May 2024, 8:59 am EST

    Hi,

    I will try out your solution. I do have another question, though. I notice that in all your examples you are comparing the binding property of the columns to determine which column is which. But what if I have multiple columns in my grid that do not have a binding?

    For example, what if I have a datasource of these objects:

    interface IUser {
      id: number,
      name: string,
      email: string,
    }

    And then I have these columns in the grid:

    • header = “”, binding = undefined, template => shows a delete button
    • header = “Id”, binding = “id”
    • header = “Name”, binding = “name”
    • header = “Email”, binding = “email”
    • header = “”, binding = undefined, template => shows an edit button

    In this example, I have two separate columns that have no binding and instead render controls. How could I tell these two columns apart if the only way to compare columns is by using the binding field?

  • Posted 3 May 2024, 7:11 am EST

    Hi,

    Actually, we need some property to uniquely identify each column while restoring the column state, so we used the column’s binding property for this purpose, you can also use a combination of the binding and header properties to uniquely identify the columns. You can also define the ‘name’ property of the columns which could be used for this purpose.

    In specific cases, as the data in the code snippet you shared, where binding is not defined for the columns as content for those columns is rendered using the ‘FlexGridCellTemplate’ only, either you can define the ‘name’ property or you can define a dummy binding for these columns, it would not affect the data or grid, and you’ll be able to use this dummy binding to identify the columns. Please refer to the following code snippet -

    header = “”, binding = 'delButtonCol', template => shows a delete button
    header = “Id”, binding = “id”
    header = “Name”, binding = “name”
    header = “Email”, binding = “email”
    header = “”, binding = '_dummyCol1',
    
    
    header = “Contact”, binding = “contact” name='contactCol'
    header = “”, binding =undefined,  name='delButtonCol', template => shows a delete button
    header = “”, binding = undefined, name="editBtnCol" template => shows an edit button

    Overall, we need a property or combination of properties that could uniquely identify each column, so that column state could be restored properly.

    Regards

  • Posted 19 August 2024, 1:06 pm EST

    I’ve been having trouble with boolean data types. I have managed to get a bool to display (by making the data type String…ugh), but cannot get the cell template to render at all when leaving it with data type boolean. On all my fields, if I have a real simple cell template that returns a two character string, the only field type that does not work is boolean. It does not get called.

    Sorry to jump on this thread, but this is the first reference to anything related to this kind of activity.

    Thanks,

    Rick

  • Posted 20 August 2024, 5:28 am EST

    Hi Rick,

    This is expected behavior for the FlexGrid that, ‘cellTemplates’ property of the Column has no effect on cells for Boolean columns, as by default input checkboxes are displayed in the grid cells containing boolean data. To apply your desired cell template on these cells, you can either set the ‘dataType’ to ‘Sring’ or you can handle the ‘formatItem’ event of the grid to make necessary changes.

    You can refer to the following sample for the same - https://stackblitz.com/edit/js-eeoyk9?file=index.js

    In case, you face any issues, please let us know.

    Regards

  • Posted 20 August 2024, 7:00 am EST

    Thank you, Vivek, for your quick response. I did have some success by setting the dataType to String and also utilizing the formatItem event of the grid. This worked great for display purposes. However, I am unable to change the edit behavior. Using Yes/No for true false, and a set of choices being Yes/No for the edit control, editing a cell already containing a value displays true or false, followed by Yes and No of the drop down. I cannot figure out how to change that behavior.

    Is there any way I can customize the Input experience on a cell on the grid? It seems to me that my only choices are to stay with the default checkbox, or change the dataType to String (and I would rather not do that creates additional work to store as a boolean) and change the input mode somewhat, but still suffering from default input behaviors.

    I would even be happy with additional properties that let’s me completely override the default behaviors.

    Thanks so much in advance for your support, not only to this question, but for all of the support you extend in this forum!

    Best,

    Rick

  • Posted 21 August 2024, 7:01 am EST

    Hi Rick,

    You can use the dataMap property of the Column and use a DataMapEditor on the column, please refer to the following updated sample for your reference - https://stackblitz.com/edit/js-sa59vc?file=index.js

    You can also refer to the following API links for more information about dataMap and dataMapEditor properties of the column -

    https://developer.mescius.com/wijmo/api/classes/wijmo_grid.column.html#datamap

    https://developer.mescius.com/wijmo/api/enums/wijmo_grid.datamapeditor.html

    In case, you still face any issues or if there is any specific type of edit implementation that you want in the grid then please let us know and share some more details about your requirements.

    Regards

Need extra support?

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

Learn More

Forum Channels