如何从表单容器中的文本字段获取和存储数据?

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> 组件,它控制状态并为 onChangeonSubmit 事件创建事件处理程序,以及一个 <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>
  );
}

哇,好长的回答!我希望你能做到这一点,如果有帮助请告诉我。