React - FlexGrid restoring persisted state clears all column templates

Posted by: jschnurer on 30 April 2024, 7:01 am EST

    • Post Options:
    • Link

    Posted 30 April 2024, 7:01 am EST - Updated 30 April 2024, 7:07 am 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, 12: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, 12: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, 1: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 1 May 2024, 9:49 pm 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 1 May 2024, 11:59 pm 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 2 May 2024, 10:11 pm 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

Need extra support?

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

Learn More

Forum Channels