如何在 MUI DataGrid 中实现可搜索/可过滤的单选列

问题描述 投票:0回答:1

我正在解决这个问题的第三天,此时我只是在原地踏步。基本上,我有两个 API 源(json 文件),我在 useEffect 中获取它们并将它们存储在数组中。这是一个庞大的、不断变化的产品列表和键/值对中的“元数据”。

文档中的这一列“国家”正是我想要实现的https://mui.com/x/react-data-grid/filtering/

我可能遇到的一个大问题是根本不了解 TypeScript,但这是迄今为止我的代码。

主页

import {React, useEffect, useState} from 'react';
import { useLocation } from 'react-router-dom';
import Button from '@mui/material/Button';
import Box from '@mui/material/Box';
import CircleIcon from '@mui/icons-material/Circle';
import Stack from '@mui/material/Stack';
import { DataGrid, useGridApiRef, GridToolbar, gridStringOrNumberComparator } from '@mui/x-data-grid';
import $ from 'jquery'
import Typography from '@mui/material/Typography';
import Skeleton from '@mui/material/Skeleton';
import Divider from '@mui/material/Divider';
import Scraper from './Scraper';
import { randomCountry, renderCountry, renderEditCountry } from '@mui/x-data-grid-generator';


// Searchable field code
// https://github.com/mui/mui-x/tree/master/packages/grid/x-data-grid-generator/src

//https://stackoverflow.com/questions/68649453/how-to-dynamically-add-the-valueoptions-of-a-singleselect-column-in-material-ui  


export default function EditQuote() {

  const location = useLocation()
  const id = location.pathname.split('quotes/')[1]
  const apiRef = useGridApiRef();
  
    const columns = [
        { field: 'id', headerName: 'ID', width: 20 },
        {
          field: 'country',
          headerName: 'Country',
          type: 'singleSelect',
          valueOptions: [],
          valueFormatter: ({ value }) => value?.label,
          generateData: randomCountry,
          renderCell: renderCountry,
          renderEditCell: renderEditCountry,
          sortComparator: (v1, v2, param1, param2) =>
            gridStringOrNumberComparator(v1.label, v2.label, param1, param2),
          width: 150,
          editable: true,
        },
        {
            field: 'model',
            headerName: 'Model',
            editable: true,
            flex: .5,
            minWidth: 100
          },
        { field: 'description', headerName: 'Description', editable: true, flex: 1, },
        {
          field: 'stock',
          headerName: 'Stock',
          editable: false,
          flex: .1,
          renderCell: (cellValues) => {
            if (cellValues.row.stock) {
              return (<CircleIcon sx={{color: '#8ced9c'}}/>)
            } else if (cellValues.row.stock === false){
              return (<CircleIcon sx={{color: '#ed8c8c'}}/>)
            } else {
              return
            }
          }
        },
        { field: 'quantity', headerName: 'Quantity', editable: true, type: 'number', flex: .25, },
        {
          field: 'price',
          headerName: 'Price',
          type: 'number',
          editable: true,
          flex: .25,
          valueGetter: (params) => {
            if (!params.value) {
              return '';
            }
            // Convert the decimal value to a percentage
            return `$${params.value}`;
          },
        },
        {
          field: 'markup',
          headerName: 'Markup',
          type: "singleSelect",
          valueOptions: [true, false],
          editable: true,
        },
        {
          field: 'total',
          headerName: 'Total',
          type: 'number',
          editable: false,
          flex: .25,
          valueGetter: (cellValues) => {
            //console.log('total', cellValues)
            return cellValues;
          },
        },
        {
            field: "Delete",
            headerName: ' ',
            renderCell: (cellValues) => {
              return (
                <Button
                  onClick={(event) => {
                    handleDel(event, cellValues);
                  }}
                >
                  X
                </Button>
              );
            }
          }
      ];

      
      
      var rows = [
      ];
    
      const handleAddRow = () => {
        var rows = apiRef.current.getRowsCount()
        apiRef.current.updateRows([{id: rows + 1, title: 'new item'}]);
      };

      const handleDel = (event, cellValues) => {
        console.log(cellValues)
        apiRef.current.updateRows([{ id: cellValues.id, _action: 'delete' }])
      };

      const Save = async () => {
        $('#status').text('Saving...')
        var rows = apiRef.current.getRowsCount()
        var json = []
        for (let r = 1; r <= rows; r++) {
            json.push(apiRef.current.getRow(r))
        }
        
        console.log(json)

        var res = await fetch('https://api.jsonbin.io/v3/b/64cb95d5b89b1e2299ca9847', {
            method: 'PUT',
            headers: {
                "X-Master-Key": sessionStorage.getItem("master"),
                "X-Access-Key": sessionStorage.getItem("key"),
                "Content-Type": "application/json"
            },
            body: JSON.stringify(json)

        })

        if (res.ok) {
          $('#status').text('Saved')
        } else {
            $('#status').text('Error')
        }

        console.log(res)
      }

      async function cellUpdate(updatedRow) {
        console.log(updatedRow)
      }

      

      async function PullRows(id) {
        await GetProducts()
        var data = await fetch('https://api.jsonbin.io/v3/b/64cb95d5b89b1e2299ca9847', {
            method: 'GET',
            headers: {
                "X-Master-Key": sessionStorage.getItem("master"),
                "X-Access-Key": sessionStorage.getItem("key"),
                "Content-Type": "application/json"
            }
        })
        var response = await data.json()
        var allQuotes = response.record
        for (let quote of allQuotes) {
          if (parseInt(id) === parseInt(quote.id)) {
            var currQuote = quote
            
          }
        }

        $('#jobname').text(currQuote.jobname)
        $('#customer').text(currQuote.customer)
        $('#description').text(currQuote.description)
        $('h1').text(`Editing Quote #${id}`)

        if (!currQuote.products) {currQuote.products = [{id: 1, item: 'No Products'}]}

        try {
          apiRef.current.updateRows(currQuote.products)
        } catch (error) {
          console.log('quote products', currQuote.products)
          console.log(error)
        }
        
      }

      var allProducts = []

      async function GetProducts() {

        // Internal Products
        var dataInternal = await fetch('https://api.jsonbin.io/v3/b/64cb00dd8e4aa6225ec9acef/latest', {
            method: 'GET',
            headers: {
                "X-Master-Key": sessionStorage.getItem("master"),
                "X-Access-Key": sessionStorage.getItem("key"),
                "Content-Type": "application/json"
            }
        })
        var responseInternal = await dataInternal.json()
        var internalProducts = responseInternal.record
        try {
            for (let row of internalProducts) {
                allProducts.push({title: row.title, model: row.model})
            }
            
        } catch (error) {
          console.log(error)
        }

        // UI Products

        var uiProducts = await Scraper() 

        for (let ui of uiProducts.products) {
          ui.item = ui.title
          allProducts.push({title: ui.title, model: ui.model})
        }

        apiRef.current.updateColumns([{field: 'title', valueOptions: allProducts}])
        console.log('all products', allProducts)
        return allProducts

      }

      useEffect(() => {
        PullRows(id)
      })

      

      
    return (
      <Box sx={{ height: 'auto', width: '88vw' }}>
        <Button href="/quotes">
          Back to Quotes
        </Button>
        <h1>Editing Quote</h1>
        <br></br>
        <Divider></Divider>
        <br></br>
        <h2 id="jobname"> </h2>
        <h4 id="customer"> </h4>
        <p id="description"> </p>
        <DataGrid
            apiRef={apiRef}
            rowRe
            rows={rows}
            columns={columns}
            autoHeight
            disableRowSelectionOnClick
            components={{
                NoRowsOverlay: () => (
                    <Box sx={{ height: 'auto', width: '88vw', padding: '30px' }}>
                        <Stack height="auto" alignItems="center" justifyContent="center">
                            Loading from database...
                        </Stack>  
                        <Typography variant="h4"><Skeleton /></Typography>          
                    </Box>
                  
                ),
                toolbar: GridToolbar 
            }}
            processRowUpdate={(updatedRow, originalRow) => { cellUpdate(updatedRow); } }
        />
        <Button size="small" onClick={handleAddRow}>
          Add a row
        </Button>
        <br></br>
        <a href="#?" type="button" id="save" onClick={Save}  className="btn btn-primary btn-lg">Save</a>
        <p id="status"></p>
      </Box>
    );
  }



我正在尝试使用

apiRef.current.updateColumns()
来更改 valueOptions。最终我的目标是从下拉列表中选择一个产品,并将后端的信息(价格、型号、描述、库存)填充到该行的其他列中。我什至不需要渲染单元格,我只需要纯文本,并且需要能够像示例中那样进行搜索。我几乎能够在自己的应用程序中复制该示例,但下拉菜单也永远不会关闭。我想如果我可以将数据加载到下拉列表中并找出内联过滤(或其他),我可以从那里获取它。

非常感谢对此的任何帮助。我已经接近了,但似乎无法弄清楚最后一块。

javascript reactjs typescript datagrid mui-x-data-grid
1个回答
0
投票

您需要使用状态来更新数据网格的行。

将代码中的

var rows = []
更改为:

const [rows, useRows] = useState([])

然后将

apiRef.current.updateRows(currQuote.products)
更改为:

setRows(currQuote.products)

之后,它应该可以工作了。通常,您不需要使用数据网格的

apiRef
,这适用于需要挂钩组件内部工作的复杂用例。

这里有一篇好文章可以帮助理解 React 中状态的重要概念:https://react.dev/learn/thinking-in-react

© www.soinside.com 2019 - 2024. All rights reserved.