我正在解决这个问题的第三天,此时我只是在原地踏步。基本上,我有两个 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。最终我的目标是从下拉列表中选择一个产品,并将后端的信息(价格、型号、描述、库存)填充到该行的其他列中。我什至不需要渲染单元格,我只需要纯文本,并且需要能够像示例中那样进行搜索。我几乎能够在自己的应用程序中复制该示例,但下拉菜单也永远不会关闭。我想如果我可以将数据加载到下拉列表中并找出内联过滤(或其他),我可以从那里获取它。
非常感谢对此的任何帮助。我已经接近了,但似乎无法弄清楚最后一块。
您需要使用状态来更新数据网格的行。
将代码中的
var rows = []
更改为:
const [rows, useRows] = useState([])
然后将
apiRef.current.updateRows(currQuote.products)
更改为:
setRows(currQuote.products)
之后,它应该可以工作了。通常,您不需要使用数据网格的
apiRef
,这适用于需要挂钩组件内部工作的复杂用例。
这里有一篇好文章可以帮助理解 React 中状态的重要概念:https://react.dev/learn/thinking-in-react