【问题标题】:Mui/DataGrid set/get filter value in Custom filter componentMui/DataGrid 在自定义过滤器组件中设置/获取过滤器值
【发布时间】:2022-10-24 21:07:41
【问题描述】:

如何在 onFilterModelChange 中看到的 mui/DataGrid 自定义过滤器设置值?

我有代码:

function MyFilterPanel() {
  const apiRef = useGridApiContext();
 
  const handleDateChange = () => {
    const { state, setState, forceUpdate } = apiRef.current;
    setState({
        filterModel: {
          items: [{ columnField: 'quantity', operatorValue: '>', value: 10000 }],
        },
    });
  };
  return (
    <DateRangePicker
      onChange={handleDateChange}
    />
  );
}
 // and
<DataGrid
    ...
        filterMode="server"
        onFilterModelChange={(newValue) => {
            console.log(newValue);
        }}
        components={{
            FilterPanel: MyFilterPanel,
        }}
/>

我有错误:

TypeError: Cannot read properties of undefined (reading 'columnVisibilityModel')

编辑。 我添加了我的代码以显示我想如何使用它。 https://codesandbox.io/s/datagrid-forked-2ulvnu

如何使用它:

  1. 点击...
  2. 选择过滤器
  3. 选择日期范围
    import * as React from "react";
    import DateRangePicker from "rsuite/DateRangePicker";
    import { DataGrid, useGridApiContext } from "@mui/x-data-grid";
    import "./ui.css"; // @import "rsuite/dist/rsuite.css";
    
    function DateRangePickerFilterPanel() {
      const apiRef = useGridApiContext();
    
      const handleDateChange = (value) => {
        // I want set here values for previous and next date
      };
      return <DateRangePicker onChange={handleDateChange} />;
    }
    
    const rows = [
      { id: "2020-01-02", col1: "Hello" },
      { id: "2020-01-03", col1: "MUI X" },
      { id: "2020-01-04", col1: "Material UI" },
      { id: "2020-01-05", col1: "MUI" },
      { id: "2020-01-06", col1: "Joy UI" },
      { id: "2020-01-07", col1: "MUI Base" }
    ];
    
    const columns = [
      { field: "id", headerName: "DateTime", type: "dateTime", width: 150 },
      { field: "col1", headerName: "Column 1", width: 150 }
    ];
    
    export default function App() {
      const [dates, setDates] = React.useState({});
    
      /*
      const getAllNames = () => {
        axiosInstance.get(`${API_PATH}/api/names?startDate=${dates.startDate}&endDate=${dates.endDate}`)
          .then((response) => {
            ...
          })
          .catch((error) => console.error(`Error: ${error}`));
      };
      */
      return (
        <div style={{ height: 300, width: "100%" }}>
          <DataGrid
            rows={rows}
            pagination
            columns={columns}
            paginationMode="server"
            rowsPerPageOptions={[10]}
            filterMode="server"
            onFilterModelChange={(newValue) => {
              // And here I want to get that data to be able
              // to pas it to backend request or somehow have acces
              // to it in fucntion App()
              console.log(newValue);
              // setDates({
              //   startDate: newValue[0],
              //   endDate: newValue[1].toISOString()
              // });
            }}
            components={{
              FilterPanel: DateRangePickerFilterPanel
            }}
          />
        </div>
      );
    }
    

【问题讨论】:

    标签: reactjs material-ui


    【解决方案1】:

    我假设您想从 DateRangePicker 获取日期并将它们传递给您的 API 调用。一旦用户选择了 dateRange,然后再次重新填充数据网格(假设基于 filterMode 设置为 server)。我已经更新了你的代码。请检查。由于您没有使用 Mui 的 FilterModel,因此您不需要 onFilterModelChange 道具。

    import DateRangePicker from "rsuite/DateRangePicker";
    import { DataGrid} from "@mui/x-data-grid";
    import "./ui.css";
    
    const getAllNames = (startDate, endDate)=> {
      console.log(startDate, endDate);
     //  Add your API call here
    };
    
    function DateRangePickerFilterPanel() {
     const handleDateChange = (value) => {
       getAllNames(value[0], value[1]);
     };
     return <DateRangePicker onChange={handleDateChange} />;
    };
    
    const rows = [
      { id: "2020-01-02", col1: "Hello" },
      { id: "2020-01-03", col1: "MUI X" },
      { id: "2020-01-04", col1: "Material UI" },
      { id: "2020-01-05", col1: "MUI" },
      { id: "2020-01-06", col1: "Joy UI" },
      { id: "2020-01-07", col1: "MUI Base" }
    ];
    
    const columns = [
      { field: "id", headerName: "DateTime", type: "dateTime", width: 150 },
      { field: "col1", headerName: "Column 1", width: 150 }
    ];
    
    export default function App() {
      /*
      const getAllNames = () => {
        axiosInstance.get(`${API_PATH}/api/names?startDate=${dates.startDate}&endDate=${dates.endDate}`)
          .then((response) => {
            ...
          })
          .catch((error) => console.error(`Error: ${error}`));
      };
      */
     
      return (
        <div style={{ height: 300, width: "100%" }}>
          <DataGrid
            rows={rows}
            pagination
            columns={columns}
            paginationMode="server"
            rowsPerPageOptions={[10]}
            filterMode="server"
            components={{
              FilterPanel: DateRangePickerFilterPanel
            }}
          />
        </div>
      );
    }
    

    【讨论】:

    • 但是如果我想将DateRangePickerFilterPanel 提取到另一个文件并调用API 保存在App 中怎么办?
    • 如果您使用任何状态管理库,例如React ContextRedux,您可以保存来自DateRangePickerhandleDateChange 的值,然后将它们传递到App API 调用中。
    【解决方案2】:

    更新:鉴于 OP 澄清他们没有使用 MUI DataGridPro 并且正在使用服务器端过滤,这里有一个解决方案:

    如果我对您的理解正确,您似乎正在尝试完成以下操作:

    1. 从 rsuite/DateRangePicker 捕获开始和结束日期,
    2. 使用DataGrid中的服务器端过滤来过滤结果

      对于#1,我们可以通过将 onChange 处理函数作为 prop 传入来捕获 DateRangePickerFilterPanel 外部的日期值。您可以使用自定义过滤器面板,并使用 DataGrid 的 components 和 componentsProps 属性将处理函数作为道具传递给它。

      function DateRangePickerFilterPanel(props) {
        // Instead of hadnling a date change here, pass the handler in as a prop.
        // This allows you access to the selected values in App. You could also use
        // a state management library like redux, but that is not shown here.
        return <DateRangePicker onChange={props.onChange} />;
      }
      
      export default function App() {
        const [dates, setDates] = useState()
      
        const handleDateChange = (newValue) => {
          // update local state with the new values
          setDates(newValue);
        }
      
        return (
          <DataGrid
            rows={rows}       // defined elsewhere
            columns={columns} // defined elsewhere
            components={{
              FilterPanel: DateRangePickerFilterPanel,
            }}
            componentsProps={{
              filterPanel: { onChange: handleDateChange }
            }}
          >
          </DataGrid>
        );
      }
      

      其次,我们想在过滤日期更新时调用服务器。我们将“日期”存储在反应状态,我们可以使用 useEffect 钩子在每次更新这些日期时调用服务器

      export default function App() {
        const [dates, setDates] = useState()
      
        useEffect( () => {
          // call the server here
        }, [dates]);
      }
      
      

      注意:服务器端文档here 表明您需要使用 onFilterModelChange 处理程序,但在这种情况下这不是必需的,因为您使用的是自定义过滤器面板。我们可以触发 DateRangePicker 的更新,我们不需要使用 onFilterModelChange。

      这是 cmets 的完整解决方案:

      import * as React from "react";
      import { DateRangePicker } from "rsuite";
      import { DataGrid, GridFilterModel } from "@mui/x-data-grid";
      import "./ui.css";
      import { fakeAxios } from "./server";
      
      function DateRangePickerFilterPanel(props) {
        // Instead of hadnling a date change here, pass the handler in as a prop.
        // This allows you access to the selected values in App. You could also use
        // a state management library like redux, but that is not shown here.
        return <DateRangePicker onChange={props.onChange} />;
      }
      
      const columns = [
        { field: "id", headerName: "ID", width: 150 },
        { field: "created", headerName: "DateTime", type: "date", width: 150 },
        { field: "col1", headerName: "Column 1", width: 150 }
      ];
      
      export default function App() {
        // These are the selected values in the date range picker. To use server
        // side filtering they must be set to the server, and the server returns
        // the filtered dataset.
        const [dates, setDates] = React.useState({});
      
        // Store the row data for the data table in react state. This will be updated
        // when you call the server API with filter parameters.
        const [rows, setRows] = React.useState([]);
      
        // Here is where we handle the date change in the filter panel. Set the dates
        // state so it can be used by the server API.
        const handleDateChange = (newValue) => {
          setDates(newValue);
        };
      
        // The rows for the datatable are loaded from the server using the dates as
        // a filter. This useEffect runs (and calls the server) everytime the value
        // of 'dates' changes.
        React.useEffect(() => {
          fakeAxios
            .get(`/api/names?startDate=${dates[0]}&endDate=${dates[1]}`)
            .then((response) => {
              console.log(
                `server called with filter; returned ${response.length} records`
              );
              setRows(response);
            });
        }, [dates]);
      
        return (
          <div style={{ height: 500, width: "100%" }}>
            <DataGrid
              rows={rows}
              pagination
              columns={columns}
              paginationMode="server"
              rowCount={10}
              rowsPerPageOptions={[10, 100]}
              filterMode="server"
              
              // onFilterModelChange is not needed since we are using a custom filter
              // panel.
      
              components={{
                FilterPanel: DateRangePickerFilterPanel
              }}
              componentsProps={{
                filterPanel: { onChange: handleDateChange }
              }}
            />
          </div>
        );
      }
      

      我正在使用“fakeAxios”对象模拟服务器响应。

      在此处查看完整的代码沙箱以获取更多详细信息: https://codesandbox.io/s/datagrid-forked-version2-koz9cy?file=/src/App.tsx


      原答案:

      tl;博士

      1. GridApi 是一项专业功能 - 使用 DataGridPro 代替 DataGrid
      2. 使用 useGridApiRef() 钩子(不是 useGridApiContext() 钩子)从 Grid 组件外部访问 GridApi

        这里的主要问题是 GridApi 是一个专业/高级功能,适用于 DataGridPro,但不适用于 DataGrid。文档对此并不是很清楚(他们自己在这里承认:https://github.com/mui/mui-x/issues/2904#issuecomment-945436602)。在DataGrid 的API 文档中,apiRef 属性不可用,但在DataGridPro 上存在。

        第二个问题是您应该使用 useGridApiRef() 而不是 useGridApiContext()。基本上,useGridApiRef() 用于从数据网格外部访问 GridApi,而 useGridApiContext() 用于从数据网格内部访问 GRidApi(它们提供了详细说明 here

        这是完成您正在寻找的代码:

        import { useState } from "react";
        import { DataGridPro, useGridApiRef } from "@mui/x-data-grid-pro";
        import { DateRangePicker, LocalizationProvider } from "@mui/x-date-pickers-pro";
        import Box from "@mui/material/Box";
        import TextField from "@mui/material/TextField";
        import { AdapterDayjs } from "@mui/x-date-pickers-pro/AdapterDayjs";
        import "./styles.css";
        
        export default function App() {
          const [value, setValue] = useState([null, null]);
          const gridApi = useGridApiRef();
        
          const rows = [
            { id: 1, col1: "Hello", col2: "World", quantity: 5000 },
            { id: 2, col1: "DataGridPro", col2: "is Awesome", quantity: 5000 },
            { id: 3, col1: "MUI", col2: "is Amazing", quantity: 12000 }
          ];
        
          const columns = [
            { field: "col1", headerName: "Column 1", width: 150 },
            { field: "col2", headerName: "Column 2", width: 150 },
            { field: "quantity", headerName: "Quantity", width: 150, type: "number" }
          ];
        
          const handleDateChange = (newValue) => {
            setValue(newValue);
        
            if (gridApi.current) {
              gridApi.current.setFilterModel({
                items: [
                  {
                    columnField: "quantity",
                    operatorValue: ">",
                    value: "10000"
                  }
                ]
              });
            }
          };
        
          return (
            <div className="App" style={{ height: "300px" }}>
              <h1>Hello CodeSandbox</h1>
              <LocalizationProvider dateAdapter={AdapterDayjs}>
                <DateRangePicker
                  value={value}
                  onChange={handleDateChange}
                  renderInput={(startProps, endProps) => (
                    <>
                      <TextField {...startProps} />
                      <Box sx={{ mx: 2 }}> to </Box>
                      <TextField {...endProps} />
                    </>
                  )}
                ></DateRangePicker>
              </LocalizationProvider>
              <DataGridPro
                apiRef={gridApi}
                rows={rows}
                columns={columns}
                onFilterModelChange={(newValue) => {
                  console.log(`received filter mode change: ${newValue}`);
                  console.log(newValue);
                }}
              ></DataGridPro>
            </div>
          );
        }
        

        此处的代码沙箱:https://codesandbox.io/s/stackoverflow-mui-datagrid-ehxesp

    【讨论】:

    • 首先我没有datagridpro。其次,我的错误,我没有准确地说明我为 DataRangePicker 使用的库。现在我添加了我的代码并注释了我想要的
    • 知道了。如果我对您的理解正确,您可以通过将 onChange 处理程序作为道具传递给 DateRangePickerFilterPanel 来实现。在此处查看代码框:codesandbox.io/s/datagrid-forked-koz9cy?file=/src/App.tsx。这更接近您正在寻找的东西吗?如果是这样,我可以相应地更新答案。
    猜你喜欢
    • 2018-10-19
    • 1970-01-01
    • 1970-01-01
    • 2018-05-06
    • 1970-01-01
    • 2016-09-23
    • 2022-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多