如何从表单容器中的文本字段获取和存储数据?
How can I get and store Data from Textfield in Form Container?
我有下面的代码用于 webtool 来编辑 Twilio 队列信息,我在从不同的表单向上传递数据时遇到问题,我如何 return 或将数据存储到保存(激活时我单击具有以下 ID 的文本字段中的保存按钮)功能(cap_percent_threshold、abandoned_target_threshold、asa_threshold、abandoned_percent_threshold)。
import React, { useRef, Component } from 'react'
import MaterialTable from 'material-table'
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import Grid from '@material-ui/core/Grid';
import axios from 'axios';
function FormRow(data) {
const queue = data.data;
const channels = queue.channels;
const tresholdData = {};
console.log('threshold data', tresholdData);
return (
channels.map((channel, index) => (<Grid container item sm={12} spacing={3} >
<Grid item sm={2} >
<TextField
autoFocus
margin="dense"
id="channel_name"
label="Channel"
type="text"
value={channel.friendly_name}
fullWidth
disabled
/>
</Grid >
<Grid item sm={2} >
<TextField
autoFocus
margin="dense"
id="cap_percent_threshold"
label="Cap Percent Threshold"
value={channel.capPercentThreshold}
type="text"
fullWidth
/>
</Grid >
<Grid item sm={2} >
<TextField
autoFocus
margin="dense"
id="abandoned_target_threshold"
label="Abandoned Target Threshold"
value={channel.abandonedTargetThreshold}
type="text"
fullWidth
/>
</Grid >
<Grid item sm={2} >
<TextField
autoFocus
margin="dense"
id="asa_threshold"
label="ASA Threshold"
value={channel.aSAThreshold}
type="text"
fullWidth
/>
</Grid >
<Grid item sm={2} >
<TextField
autoFocus
margin="dense"
id="abandoned_percent_threshold"
label="Abandoned Percent Threshold"
value={channel.abandonedPercentThreshold}
onChage={input => tresholdData += input}
type="text"
fullWidth
/>
</Grid >
</Grid >
))
);
}
function FormContainer(data) {
console.log('what');
console.log(data);
const queue = data;
const cancel = () => {
console.log('cancel');
}
const save = (event) => {
console.log('save');
console.log(': ', event);
console.log('Data on save: ', data);
const postURL = `https://taskrouter.twilio.com/v1/Workspaces/${workspaceSid}/TaskQueues/${testTaskQueue}`
/*
const updateTaskQueuePromise = axios.post(postURL, {
apiKey: this.props.credentials().apiKey,
apiSecret: this.props.credentials().apiSecret,
}).then(result => {
console.log('Result from Save Axios: ', result)
}
*/
}
return (
<div>
<Grid container >
<Grid item sm={1} ></Grid >
<Grid container item sm={11} spacing={3} >
<FormRow data={queue} />
</Grid>
<Grid container
direction="row"
justify="flexend"
alignItems="center">
<Grid item sm={1} ></Grid >
<Grid item sm={11} spacing={3} justify="flexend" alignItems="center" >
<Button variant="outlined" onClick={cancel} color="secondary" > Cancel</Button >
<Button variant="outlined" onClick={save} color="primary" > Save</Button >
</Grid >
</Grid >
</Grid >
</div >
);
}
export default class Editor extends React.Component {
constructor(props) {
super(props);
/*
_handleTextFieldChange: function (e) {
this.setState({
textFieldValue: e.target.value
});
*/
this.state = {
open: false,
queue: {},
columns: [
{ title: 'Queue Name', field: 'friendly_name', editable: 'never' },
{ title: 'Channel', field: 'channel_friendly_name', editable: 'never' },
{ title: 'Cap Percent Threshold', field: 'capPercentThreshold', type: 'numeric' },
{ title: 'Abandoned Target Threshold', field: 'abandonedTargetThreshold', type: 'numeric' },
{ title: 'ASA Threshold', field: 'aSAThreshold', type: 'numeric' },
{ title: 'Abondended Percent Threshold', field: 'abandonedPercentThreshold', type: 'numeric' }
//{ title: 'Reset Timezone', field: 'channel_reset_timezone' },
//{ title: 'Reset Time', field: 'channel_reset_time' },
// { title: 'Service Level Threshold', field: 'channel_service_level_threshold', type: 'numeric' },
//{ title: 'Short Abandoned Threshold', field: 'channel_service_level_threshold', type: 'numeric' },
//Added to Edit the color Changing Thresholds
]
}
this.handleClose = this.handleClose.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleClose(xxx) {
console.log('this happening');
console.log(xxx);
this.setState({ open: true });
}
handleChange(event) {
this.setState({ textFieldValue: event.target.value });
}
handleSubmit(event) {
alert('An essay was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
console.log('Render this props.data!!');
console.group(this.props.data);
const queues = this.props.data.map(q => {
const channel = q.channels.find(c => c.unique_name === 'voice');
q.channel_friendly_name = channel.friendly_name;
q.channel_reset_time = channel.reset_time;
//q.channel_reset_timezone = channel.reset_timezone;
//q.channel_service_level_threshold = channel.service_level_threshold;
//Added To Edit the Color Changing Thresholds:
q.channel_service_level_threshold = channel.capPercentThreshold;
q.channel_service_level_threshold = channel.abandonedTargetThreshold;
q.channel_service_level_threshold = channel.aSAThreshold;
q.channel_service_level_threshold = channel.abandonedPercentThreshold;
return q;
})
return (
<div>
<MaterialTable
title='Queue SLA'
columns={this.state.columns}
data={queues}
options={
{
pageSize: 25,
pageSizeOptions: [25, 100, 500, 1000]
}
}
detailPanel={
rowData => {
return FormContainer(rowData);
}
}
/>
</div >
);
}
}
此处为 Twilio 开发人员布道师。
您的表单结构和其中的数据似乎比较随意。我知道它基于您从 TaskRouter API 获得的任何数据,这些数据可能不会改变,但您仍然必须呈现表单项列表。为此,我编写了一个快速实验,看看如何为可以提供组件的任何对象解决这个问题。希望这对您有所帮助,您可以将其应用到您的应用程序中。
React 的模型是数据向下发送而操作向上冒泡,所以在这种情况下,您希望将表单的状态存储在顶层组件中,该组件还处理表单的提交事件,并将该状态向下发送到子组件。然后子组件应该发送更新状态的更改事件。
然后,当提交表单时,您可以将完整状态发送到端点(此处附注,我们建议您在服务器端实现对 Twilio API 的调用,而不是在客户端中,这样您就不会将您的 API 凭据嵌入到 front-end 可以获取它们的地方)。我注意到您的应用程序目前没有 <form>
元素(我可以看到),我建议您将表单元素包装在 <form>
中,以便您可以处理 [=19] 的提交=] 与 onSubmit
处理程序。
上一段是比较容易的部分,比较难的部分是保持状态是最新的。如果你 运行 你的代码,其中一些输入有 value
属性但没有 onChange
处理程序,你会发现 React 告诉你你需要做关于它的一些东西。
因此,您需要实施一个 onChange
处理程序来更新顶级组件中的状态。对于一个充满数据的任意对象,我自己尝试实现了这一点。我创建了一个 handleUpdate
函数,它可以作为 onChange
处理程序传递给任何输入。当 handleUpdate
函数被触发时,它会传递一个指向触发它的 DOM 元素的事件。您可以使用它来获取元素的值来更新您的状态:
const handleUpdate = (event) => {
const element = event.target;
const value = element.value;
console.log(value);
// we'll come back to this
}
现在我们有了更新状态所需的值。为此,我们需要知道原始值在状态中的位置,并替换它。我在我的实验中处理了这个问题,方法是构建一个由遍历对象所需的键组成的字符串,并编写一个函数来克隆原始状态,取消选择键并更新值,然后返回可以设置为新状态的内容.该函数如下所示:
function updateObject(obj, keys, value) {
// deep copy the state object
obj = JSON.parse(JSON.stringify(obj));
let innerObj = obj;
// Follow the keys to the value that needs to be updated
for (var i = 0; i < keys.length - 1; i++) {
innerObj = innerObj[keys[i]];
}
// Update the value
innerObj[keys[i]] = value;
// Return the new state object
return obj;
}
因此,如果您有一个如下所示的状态对象:
const state = { "foo": { "bar": "baz" } };
而你运行上面的函数是这样的:
updateObject(state, "foo.bar", "qux");
// => { "foo": { "bar": "qux" }}
这也适用于数组,因为数组的索引只是对象中的字符串键:
const state = { "foo": [{ "bar": "baz" }] };
updateObject(state, "foo.0.bar", "qux");
// => { "foo": [{ "bar": "qux" }]}
使用 updateObject
函数,我们现在可以更新状态中的任何值,只要我们有该对象的路径即可。在你的情况下,你可能能够以更简单的方式实现这一点,但在我的实验中,我建立了路径,因为我递归遍历状态对象,传递 handleUpdate
函数并最终使路径成为 name
我的输入元素上的属性。 (构建的路径字符串也可用作 React 的 key
属性。)
handleUpdate
函数本身变为:
const handleUpdate = (event) => {
// Find the keys of the object that lead to the value that needs to be changed
const path = event.target.name;
const keys = path.split(".");
// Update the state
setData(updateObject(data, keys, event.target.value));
};
总的来说,我创建了两个组件,一个外部 <Form>
组件,它控制状态并为 onChange
和 onSubmit
事件创建事件处理程序,以及一个 <FormRow>
以递归方式呈现状态中的数据。这是这两个组件:
import React, { useState } from "react";
function FormRow({ path, value, handleUpdate, label }) {
if (typeof value === "string") {
// If the value is a string, render a label and input
return (
<div key={path}>
<label>{label}</label>
<input
type="text"
value={value}
onChange={handleUpdate}
name={path}
></input>
</div>
);
} else {
// Otherwise, assume the value is an array and render a new FormRow for each
// item in the array
return value.map((obj, i) => {
return (
<div
style={{
marginLeft: "10px",
borderLeft: "2px solid #000",
}}
key={`${path}-${i}`}
>
{Object.keys(obj).map((key) => (
<FormRow
key={`${path}.${i}.${key}`}
// We construct a path here that is a map of keys/indices to the
// value from the root of the original object
path={`${path}.${i}.${key}`}
value={obj[key]}
handleUpdate={handleUpdate}
label={key}
/>
))}
</div>
);
});
}
}
function Form({ startingData }) {
const [data, setData] = useState(startingData);
function updateObject(obj, keys, value) {
// deep copy the state object
obj = JSON.parse(JSON.stringify(obj));
let innerObj = obj;
// Follow the keys to the value that needs to be updated
for (var i = 0; i < keys.length - 1; i++) {
innerObj = innerObj[keys[i]];
}
// Update the value
innerObj[keys[i]] = value;
// Return the new state object
return obj;
}
const handleUpdate = (event) => {
// Find the keys of the object that lead to the value that needs to be changed
const path = event.target.name;
const keys = path.split(".");
// Update the state
setData(updateObject(data, keys, event.target.value));
};
const handleSubmit = (event) => {
event.preventDefault();
console.log("Data submitted: ", data);
};
return (
<form onSubmit={handleSubmit}>
{Object.keys(data).map((key, i) => (
<FormRow
key={`${key}-${i}`}
path={key}
value={data[key]}
handleUpdate={handleUpdate}
label={key}
/>
))}
<button type="submit">Save</button>
</form>
);
}
export default Form;
然后您可以呈现 <Form>
组件,向其传递一个 startingData
道具,它将呈现所需的整个表单和提交它的处理程序。
最终,对于您的实施,您可能能够保留现有结构并在 <FormContainer>
元素中实施类似的 handleUpdate
函数,并使用 <TextField>
呈现每个一个 name
道具,可以指向状态中的原始值和 handleUpdate
函数。
类似于:
return (
channels.map((channel, index) => (<Grid container item sm={12} spacing={3} >
<Grid item sm={2} >
<TextField
autoFocus
margin="dense"
id="channel_name"
label="Channel"
type="text"
fullWidth
disabled
value={channel.friendly_name}
onChange={handleUpdate}
name={`data.channels.${index}.friendly_name`}
/>
</Grid >
// more Grid items
然后 <FormContainer>
可能看起来像这样(我添加了一个 <form>
,创建了一个 handleUpdate
函数并将其传递给 FormRow
):
function FormContainer(data) {
const [queue, setQueue] = useState(data);
const cancel = (event) => {
event.preventDefault();
console.log("cancel");
};
const save = (event) => {
event.preventDefault();
// save the data in the state
};
const updateObject = (obj, keys, value) => {
obj = JSON.parse(JSON.stringify(obj));
let innerObj = obj;
for (var i = 0; i < keys.length - 1; i++) {
innerObj = innerObj[keys[i]];
}
innerObj[keys[i]] = value;
return obj;
}
const handleUpdate = (event) => {
const path = event.target.name;
const keys = path.split(".");
setQueue(updateObject(queue, keys, event.target.value));
};
return (
<form onSubmit={save}>
<Grid container>
<Grid item sm={1}></Grid>
<Grid container item sm={11} spacing={3}>
<FormRow data={queue} handleUpdate={handleUpdate} />
</Grid>
<Grid container direction="row" justify="flexend" alignItems="center">
<Grid item sm={1}></Grid>
<Grid item sm={11} spacing={3} justify="flexend" alignItems="center">
<Button variant="outlined" onClick={cancel} color="secondary">
{" "}
Cancel
</Button>
<Button variant="outlined" color="primary">
{" "}
Save
</Button>
</Grid>
</Grid>
</Grid>
</form>
);
}
哇,好长的回答!我希望你能做到这一点,如果有帮助请告诉我。
我有下面的代码用于 webtool 来编辑 Twilio 队列信息,我在从不同的表单向上传递数据时遇到问题,我如何 return 或将数据存储到保存(激活时我单击具有以下 ID 的文本字段中的保存按钮)功能(cap_percent_threshold、abandoned_target_threshold、asa_threshold、abandoned_percent_threshold)。
import React, { useRef, Component } from 'react'
import MaterialTable from 'material-table'
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import Grid from '@material-ui/core/Grid';
import axios from 'axios';
function FormRow(data) {
const queue = data.data;
const channels = queue.channels;
const tresholdData = {};
console.log('threshold data', tresholdData);
return (
channels.map((channel, index) => (<Grid container item sm={12} spacing={3} >
<Grid item sm={2} >
<TextField
autoFocus
margin="dense"
id="channel_name"
label="Channel"
type="text"
value={channel.friendly_name}
fullWidth
disabled
/>
</Grid >
<Grid item sm={2} >
<TextField
autoFocus
margin="dense"
id="cap_percent_threshold"
label="Cap Percent Threshold"
value={channel.capPercentThreshold}
type="text"
fullWidth
/>
</Grid >
<Grid item sm={2} >
<TextField
autoFocus
margin="dense"
id="abandoned_target_threshold"
label="Abandoned Target Threshold"
value={channel.abandonedTargetThreshold}
type="text"
fullWidth
/>
</Grid >
<Grid item sm={2} >
<TextField
autoFocus
margin="dense"
id="asa_threshold"
label="ASA Threshold"
value={channel.aSAThreshold}
type="text"
fullWidth
/>
</Grid >
<Grid item sm={2} >
<TextField
autoFocus
margin="dense"
id="abandoned_percent_threshold"
label="Abandoned Percent Threshold"
value={channel.abandonedPercentThreshold}
onChage={input => tresholdData += input}
type="text"
fullWidth
/>
</Grid >
</Grid >
))
);
}
function FormContainer(data) {
console.log('what');
console.log(data);
const queue = data;
const cancel = () => {
console.log('cancel');
}
const save = (event) => {
console.log('save');
console.log(': ', event);
console.log('Data on save: ', data);
const postURL = `https://taskrouter.twilio.com/v1/Workspaces/${workspaceSid}/TaskQueues/${testTaskQueue}`
/*
const updateTaskQueuePromise = axios.post(postURL, {
apiKey: this.props.credentials().apiKey,
apiSecret: this.props.credentials().apiSecret,
}).then(result => {
console.log('Result from Save Axios: ', result)
}
*/
}
return (
<div>
<Grid container >
<Grid item sm={1} ></Grid >
<Grid container item sm={11} spacing={3} >
<FormRow data={queue} />
</Grid>
<Grid container
direction="row"
justify="flexend"
alignItems="center">
<Grid item sm={1} ></Grid >
<Grid item sm={11} spacing={3} justify="flexend" alignItems="center" >
<Button variant="outlined" onClick={cancel} color="secondary" > Cancel</Button >
<Button variant="outlined" onClick={save} color="primary" > Save</Button >
</Grid >
</Grid >
</Grid >
</div >
);
}
export default class Editor extends React.Component {
constructor(props) {
super(props);
/*
_handleTextFieldChange: function (e) {
this.setState({
textFieldValue: e.target.value
});
*/
this.state = {
open: false,
queue: {},
columns: [
{ title: 'Queue Name', field: 'friendly_name', editable: 'never' },
{ title: 'Channel', field: 'channel_friendly_name', editable: 'never' },
{ title: 'Cap Percent Threshold', field: 'capPercentThreshold', type: 'numeric' },
{ title: 'Abandoned Target Threshold', field: 'abandonedTargetThreshold', type: 'numeric' },
{ title: 'ASA Threshold', field: 'aSAThreshold', type: 'numeric' },
{ title: 'Abondended Percent Threshold', field: 'abandonedPercentThreshold', type: 'numeric' }
//{ title: 'Reset Timezone', field: 'channel_reset_timezone' },
//{ title: 'Reset Time', field: 'channel_reset_time' },
// { title: 'Service Level Threshold', field: 'channel_service_level_threshold', type: 'numeric' },
//{ title: 'Short Abandoned Threshold', field: 'channel_service_level_threshold', type: 'numeric' },
//Added to Edit the color Changing Thresholds
]
}
this.handleClose = this.handleClose.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleClose(xxx) {
console.log('this happening');
console.log(xxx);
this.setState({ open: true });
}
handleChange(event) {
this.setState({ textFieldValue: event.target.value });
}
handleSubmit(event) {
alert('An essay was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
console.log('Render this props.data!!');
console.group(this.props.data);
const queues = this.props.data.map(q => {
const channel = q.channels.find(c => c.unique_name === 'voice');
q.channel_friendly_name = channel.friendly_name;
q.channel_reset_time = channel.reset_time;
//q.channel_reset_timezone = channel.reset_timezone;
//q.channel_service_level_threshold = channel.service_level_threshold;
//Added To Edit the Color Changing Thresholds:
q.channel_service_level_threshold = channel.capPercentThreshold;
q.channel_service_level_threshold = channel.abandonedTargetThreshold;
q.channel_service_level_threshold = channel.aSAThreshold;
q.channel_service_level_threshold = channel.abandonedPercentThreshold;
return q;
})
return (
<div>
<MaterialTable
title='Queue SLA'
columns={this.state.columns}
data={queues}
options={
{
pageSize: 25,
pageSizeOptions: [25, 100, 500, 1000]
}
}
detailPanel={
rowData => {
return FormContainer(rowData);
}
}
/>
</div >
);
}
}
此处为 Twilio 开发人员布道师。
您的表单结构和其中的数据似乎比较随意。我知道它基于您从 TaskRouter API 获得的任何数据,这些数据可能不会改变,但您仍然必须呈现表单项列表。为此,我编写了一个快速实验,看看如何为可以提供组件的任何对象解决这个问题。希望这对您有所帮助,您可以将其应用到您的应用程序中。
React 的模型是数据向下发送而操作向上冒泡,所以在这种情况下,您希望将表单的状态存储在顶层组件中,该组件还处理表单的提交事件,并将该状态向下发送到子组件。然后子组件应该发送更新状态的更改事件。
然后,当提交表单时,您可以将完整状态发送到端点(此处附注,我们建议您在服务器端实现对 Twilio API 的调用,而不是在客户端中,这样您就不会将您的 API 凭据嵌入到 front-end 可以获取它们的地方)。我注意到您的应用程序目前没有 <form>
元素(我可以看到),我建议您将表单元素包装在 <form>
中,以便您可以处理 [=19] 的提交=] 与 onSubmit
处理程序。
上一段是比较容易的部分,比较难的部分是保持状态是最新的。如果你 运行 你的代码,其中一些输入有 value
属性但没有 onChange
处理程序,你会发现 React 告诉你你需要做关于它的一些东西。
因此,您需要实施一个 onChange
处理程序来更新顶级组件中的状态。对于一个充满数据的任意对象,我自己尝试实现了这一点。我创建了一个 handleUpdate
函数,它可以作为 onChange
处理程序传递给任何输入。当 handleUpdate
函数被触发时,它会传递一个指向触发它的 DOM 元素的事件。您可以使用它来获取元素的值来更新您的状态:
const handleUpdate = (event) => {
const element = event.target;
const value = element.value;
console.log(value);
// we'll come back to this
}
现在我们有了更新状态所需的值。为此,我们需要知道原始值在状态中的位置,并替换它。我在我的实验中处理了这个问题,方法是构建一个由遍历对象所需的键组成的字符串,并编写一个函数来克隆原始状态,取消选择键并更新值,然后返回可以设置为新状态的内容.该函数如下所示:
function updateObject(obj, keys, value) {
// deep copy the state object
obj = JSON.parse(JSON.stringify(obj));
let innerObj = obj;
// Follow the keys to the value that needs to be updated
for (var i = 0; i < keys.length - 1; i++) {
innerObj = innerObj[keys[i]];
}
// Update the value
innerObj[keys[i]] = value;
// Return the new state object
return obj;
}
因此,如果您有一个如下所示的状态对象:
const state = { "foo": { "bar": "baz" } };
而你运行上面的函数是这样的:
updateObject(state, "foo.bar", "qux");
// => { "foo": { "bar": "qux" }}
这也适用于数组,因为数组的索引只是对象中的字符串键:
const state = { "foo": [{ "bar": "baz" }] };
updateObject(state, "foo.0.bar", "qux");
// => { "foo": [{ "bar": "qux" }]}
使用 updateObject
函数,我们现在可以更新状态中的任何值,只要我们有该对象的路径即可。在你的情况下,你可能能够以更简单的方式实现这一点,但在我的实验中,我建立了路径,因为我递归遍历状态对象,传递 handleUpdate
函数并最终使路径成为 name
我的输入元素上的属性。 (构建的路径字符串也可用作 React 的 key
属性。)
handleUpdate
函数本身变为:
const handleUpdate = (event) => {
// Find the keys of the object that lead to the value that needs to be changed
const path = event.target.name;
const keys = path.split(".");
// Update the state
setData(updateObject(data, keys, event.target.value));
};
总的来说,我创建了两个组件,一个外部 <Form>
组件,它控制状态并为 onChange
和 onSubmit
事件创建事件处理程序,以及一个 <FormRow>
以递归方式呈现状态中的数据。这是这两个组件:
import React, { useState } from "react";
function FormRow({ path, value, handleUpdate, label }) {
if (typeof value === "string") {
// If the value is a string, render a label and input
return (
<div key={path}>
<label>{label}</label>
<input
type="text"
value={value}
onChange={handleUpdate}
name={path}
></input>
</div>
);
} else {
// Otherwise, assume the value is an array and render a new FormRow for each
// item in the array
return value.map((obj, i) => {
return (
<div
style={{
marginLeft: "10px",
borderLeft: "2px solid #000",
}}
key={`${path}-${i}`}
>
{Object.keys(obj).map((key) => (
<FormRow
key={`${path}.${i}.${key}`}
// We construct a path here that is a map of keys/indices to the
// value from the root of the original object
path={`${path}.${i}.${key}`}
value={obj[key]}
handleUpdate={handleUpdate}
label={key}
/>
))}
</div>
);
});
}
}
function Form({ startingData }) {
const [data, setData] = useState(startingData);
function updateObject(obj, keys, value) {
// deep copy the state object
obj = JSON.parse(JSON.stringify(obj));
let innerObj = obj;
// Follow the keys to the value that needs to be updated
for (var i = 0; i < keys.length - 1; i++) {
innerObj = innerObj[keys[i]];
}
// Update the value
innerObj[keys[i]] = value;
// Return the new state object
return obj;
}
const handleUpdate = (event) => {
// Find the keys of the object that lead to the value that needs to be changed
const path = event.target.name;
const keys = path.split(".");
// Update the state
setData(updateObject(data, keys, event.target.value));
};
const handleSubmit = (event) => {
event.preventDefault();
console.log("Data submitted: ", data);
};
return (
<form onSubmit={handleSubmit}>
{Object.keys(data).map((key, i) => (
<FormRow
key={`${key}-${i}`}
path={key}
value={data[key]}
handleUpdate={handleUpdate}
label={key}
/>
))}
<button type="submit">Save</button>
</form>
);
}
export default Form;
然后您可以呈现 <Form>
组件,向其传递一个 startingData
道具,它将呈现所需的整个表单和提交它的处理程序。
最终,对于您的实施,您可能能够保留现有结构并在 <FormContainer>
元素中实施类似的 handleUpdate
函数,并使用 <TextField>
呈现每个一个 name
道具,可以指向状态中的原始值和 handleUpdate
函数。
类似于:
return (
channels.map((channel, index) => (<Grid container item sm={12} spacing={3} >
<Grid item sm={2} >
<TextField
autoFocus
margin="dense"
id="channel_name"
label="Channel"
type="text"
fullWidth
disabled
value={channel.friendly_name}
onChange={handleUpdate}
name={`data.channels.${index}.friendly_name`}
/>
</Grid >
// more Grid items
然后 <FormContainer>
可能看起来像这样(我添加了一个 <form>
,创建了一个 handleUpdate
函数并将其传递给 FormRow
):
function FormContainer(data) {
const [queue, setQueue] = useState(data);
const cancel = (event) => {
event.preventDefault();
console.log("cancel");
};
const save = (event) => {
event.preventDefault();
// save the data in the state
};
const updateObject = (obj, keys, value) => {
obj = JSON.parse(JSON.stringify(obj));
let innerObj = obj;
for (var i = 0; i < keys.length - 1; i++) {
innerObj = innerObj[keys[i]];
}
innerObj[keys[i]] = value;
return obj;
}
const handleUpdate = (event) => {
const path = event.target.name;
const keys = path.split(".");
setQueue(updateObject(queue, keys, event.target.value));
};
return (
<form onSubmit={save}>
<Grid container>
<Grid item sm={1}></Grid>
<Grid container item sm={11} spacing={3}>
<FormRow data={queue} handleUpdate={handleUpdate} />
</Grid>
<Grid container direction="row" justify="flexend" alignItems="center">
<Grid item sm={1}></Grid>
<Grid item sm={11} spacing={3} justify="flexend" alignItems="center">
<Button variant="outlined" onClick={cancel} color="secondary">
{" "}
Cancel
</Button>
<Button variant="outlined" color="primary">
{" "}
Save
</Button>
</Grid>
</Grid>
</Grid>
</form>
);
}
哇,好长的回答!我希望你能做到这一点,如果有帮助请告诉我。