Search code examples
reactjsmaterial-table

Material Table grouped view add row doesn't show grouped field


I am using Grouping in material table. In the group view when I try to add a new row , it doesn't show grouped column.

In following example , I am doing column grouping based on "Geography" column. In the grouped view when I try to "Add" new records ,I don't see Geography field. However when I remove all grouping , it works as expected.

import React from "react";
import MaterialTable, { MTableToolbar } from "material-table";

export default function MatTableDemo() {

  const columns = [
    { title: "Geography", field: "geo", defaultGroupOrder: 0, editable: "onAdd" },
    { title: "Prouct Category", field: "prodCat", editable: "onAdd" },
    { title: "Items", field: "item", grouping: false, editable: "onAdd" },
  ];

  const data = [
    {
      id: 1,
      geo: "Canada",
      prodCat: "Food",
      item: "Dairy",
    },
    {
      id: 2,
      geo: "Europe",
      prodCat: "Food",
      item: "Dairy",
    },

  ];

  return (
    <MaterialTable
      title="Grouped Table"
      columns={columns}
      data={data}
      editable={{
        onRowAdd: (newData) => {
          return new Promise((resolve) => {
            //handleRowAdd(newData, resolve)
          })
        },
      }}
      options={{
        grouping: true,
        paging: false,
      }}
    />
  )
}





Solution

  • I'm not aware of an easy way to achieve what you are looking for, also I found a few closed or stall issues regarding this behavior on the official repository (link1 or link2). Based on the Duplicate Action Preview example at the docs, I found two different workarounds, boths using tableRef and initialFormData props.

    Remove all grouping applied and create the new row

    enter image description here

    I defined a custom action that allows removing the applied filters before presenting the addition row. This is done by performing a re-rendering from setting a different initialFormData value.

    actions={[
              {
                icon: "library_add",
                tooltip: "Create User",
                onClick: (event, rowData) => {
                  const materialTable = tableRef.current;
                  setInitialFormData({});
    
                  materialTable.setState({
                    ...materialTable.dataManager.getRenderState(),
                    showAddRow: true
                  });
                }
              }
            ]}
    

    This works without using defaultGroupOrder on column definition and requires at least one column with a render prop defined.

    const tableColumns = [
        {
          title: "Category", field: "category"
        },
        { title: "Name", field: "name" },
        {
          title: "Password",
          field: "password",
          grouping: false,
          render: (rowData) => (
            <input type="password" value={rowData.password} readOnly />
          )
        }
      ];
    

    Use a default column value while creating the new row

    enter image description here

    The same approach as before, but initialFormData now may take the value of the category (is taken from rowData), so when the addRow is rendered, the field is not shown but the value is set.

    actions={[
              {
                icon: "library_add",
                tooltip: "Create User",
    
                onClick: (event, rowData) => {
                  const materialTable = tableRef.current;
                  setInitialFormData({
                    category: rowData.category // 2nd approach
                  });
    
                  materialTable.setState({
                    ...materialTable.dataManager.getRenderState(),
                    showAddRow: true
                  });
                }
              }
            ]}
    

    Works with the defaultGroupOrder on column definition:

    const tableColumns = [
        {
          title: "Category",
          field: "category",
          defaultGroupOrder: 0 // 2nd approach
        },
        { title: "Name", field: "name" },
        {
          title: "Password",
          field: "password",
          grouping: false,
          render: (rowData) => (
            <input type="password" value={rowData.password} readOnly />
          )
        }
      ];
    

    The second approach may look better but it's a bit tricky for the user since the addRow always spawns as the 'last' or 'first' row of the table.

    To avoid rendering the default **add action button ** on the top right of the table, I use a custom action component to overwrite it, like this:

    components={{
              Action: (props) => {
                //If isn't the add action
                if (
                  typeof props.action === typeof Function ||
                  props.action.tooltip !== "Add"
                ) {
                  return <MTableAction {...props} />;
                } else {
                  return <></>;
                }
              }
            }}
    

    Definitely, this solution feels a little bit hacky, but as I said before I think they are workarounds until the feature is included on the main component. Hopefully, someone can improve over these proposals.

    Hope that works for you! Sandbox here.