【问题标题】:Antd Select form item keyboard behavior problemAntd Select表单项键盘行为问题
【发布时间】:2023-03-14 20:14:01
【问题描述】:

我有一个表格,大部分情况下都可以正常工作。但是让我感到困扰的一件事是用于估算一个人的状态的 Select 项(请参见下面的屏幕截图)。它可以用鼠标很好地工作,但是如果您在表单和选项卡中切换到州字段,然后键入加利福尼亚州的“CA”之类的州,然后切换到下一个字段(邮政编码),州字段将重置为空白.如果它匹配选择器的选项之一,我希望它保持填充 CA。

如果我输入“CA”并按回车键前进到下一个字段,它确实工作,但这对于表单的正常工作方式似乎并不直观。如果我只使用鼠标选择字段,它也可以工作。我一直在查看 Select 上的 antd 文档,但没有看到任何可能控制这一点的东西。也许我遗漏了一些东西,我需要做一些 onchange 函数?

无论如何,这似乎是一个相当简单的问题,但解决方案却让我望而却步。

选择表单项的代码:

                              <Form.Item name={[field.name, "state"]} fieldKey={[field.fieldKey, "state"]} rules={rules}>
                                <Select
                                  showSearch
                                  placeholder="State"
                                  defaultActiveFirstOption={true}
                                >
                                  <Option value="AL">AL</Option>
                                  <Option value="AK">AK</Option>
                                  <Option value="AZ">AZ</Option>
                                  <Option value="AR">AR</Option>
                                  <Option value="CA">CA</Option>
                                  <Option value="CO">CO</Option>
                                  <Option value="CT">CT</Option>
                                  <Option value="DE">DE</Option>
                                  <Option value="FL">FL</Option>
                                  <Option value="GL">GA</Option>
                                  <Option value="HI">HI</Option>
                                  <Option value="ID">ID</Option>
                                  <Option value="IL">IL</Option>
                                  <Option value="IN">IN</Option>
                                  <Option value="IA">IA</Option>
                                  <Option value="KS">KS</Option>
                                  <Option value="KY">KY</Option>
                                  <Option value="LA">LA</Option>
                                  <Option value="ME">ME</Option>
                                  <Option value="MD">MD</Option>
                                  <Option value="MA">MA</Option>
                                  <Option value="MI">MI</Option>
                                  <Option value="MN">MN</Option>
                                  <Option value="MS">MS</Option>
                                  <Option value="MO">MO</Option>
                                  <Option value="MT">MT</Option>
                                  <Option value="NE">NE</Option>
                                  <Option value="NV">NV</Option>
                                  <Option value="NH">NH</Option>
                                  <Option value="NJ">NJ</Option>
                                  <Option value="NM">NM</Option>
                                  <Option value="NY">NY</Option>
                                  <Option value="NC">NC</Option>
                                  <Option value="ND">ND</Option>
                                  <Option value="OH">OH</Option>
                                  <Option value="OK">OK</Option>
                                  <Option value="OR">OR</Option>
                                  <Option value="PA">PA</Option>
                                  <Option value="RI">RI</Option>
                                  <Option value="SC">SC</Option>
                                  <Option value="SD">SD</Option>
                                  <Option value="TN">TN</Option>
                                  <Option value="TX">TX</Option>
                                  <Option value="UT">UT</Option>
                                  <Option value="VT">VT</Option>
                                  <Option value="VA">VA</Option>
                                  <Option value="WA">WA</Option>
                                  <Option value="WV">WV</Option>
                                  <Option value="WI">WI</Option>
                                  <Option value="WY">WY</Option>
                                  <Option value="AS">AS</Option>
                                  <Option value="DC">DC</Option>
                                  <Option value="FM">FM</Option>
                                  <Option value="GU">GU</Option>
                                  <Option value="MH">MH</Option>
                                  <Option value="MP">MP</Option>
                                  <Option value="PW">PW</Option>
                                  <Option value="PR">PR</Option>
                                  <Option value="VI">VI</Option>
                                </Select>
                              </Form.Item>

更新 #1

因此,按照 cmets 中的建议(我注意到表单控制部分下的类似方法 here),我在课堂上添加了 formRef = React.createRef();

然后在我的表单标签中添加了一个ref={this.formRef} 标签。然后通过添加触发 setFieldsValue 以设置输入的大写版本的 onBlur 标记来修改我的 Select。

这编译并运行良好。它甚至输出 console.log 语句。但是由于某种原因它没有设置字段值,我不确定为什么。

    return (
      <Form
**      ref={this.formRef}
        name="dynamic_form_item"
        {...formItemLayoutWithOutLabel}
        onFinish={onFinish}
        id="myForm"
        validateMessages={validateMessages}
      >
        {
          <Form.List name="applicants">
            {(fields, { add, remove }) => {
              return (
                <div>
                  {fields.map((field, index) => (
                    <Form.Item {...formItemLayout} label={`Applicant #${index + 1}`} key={field.key}>
                              <Form.Item name={[field.name, "state"]} fieldKey={[field.fieldKey, "state"]} rules={rules}>
                                <Select
                                  showSearch
                                  placeholder="State"
                                  defaultActiveFirstOption={true}
**                                onBlur={(event) => {
**                                  console.log("onblur: ", event);
**                                  this.formRef.current.setFieldsValue({ state: event.target.value.toUpperCase() });
**                                }}
                                >

更新 #2

调用 onFinish 时的值的屏幕截图。请注意,这是一个动态表单,用户可以通过单击添加申请人按钮来添加其他申请人。每个申请人都有显示的字段,包括他们居住的州。

    const onFinish = (values) => {
      console.log("onFinish: ", values);
      this.props.onSubmit({
        applicants: values.applicants,
      });
    };

更新 #3

试图模仿这种方法,但是由于某种原因,在由值返回的对象中,状态不存在,最终我得到一个未捕获的类型错误并且状态没有填充。

                              <Form.Item name={[field.name, "state"]} fieldKey={[field.fieldKey, "state"]} rules={rules}>
                                <Select
                                  showSearch
                                  placeholder="State"
                                  defaultActiveFirstOption={true}
                                  onBlur={(event) => {
                                    console.log("values", this.formRef.current.getFieldsValue());
                                    console.log("target:", event.target);
                                    console.log("id", event.target.id);
                                    const stateIndex = event.target.getAttribute("aria-activedescendant").split("_").slice(-1);
                                    const values = this.formRef.current.getFieldsValue();
                                    const namePath = event.target.id.split("_");
                                    console.log("namePath", namePath);
                                    values[namePath[0]][namePath[1]][namePath[2]] = event.target.value.toUpperCase() || STATES[stateIndex];
                                    this.formRef.current.setFieldsValue(values);
                                  }}
                                >
                                  {STATES.map((state) => (
                                    <Option value={state} key={state}>
                                      {state}
                                    </Option>
                                  ))}
                                </Select>
                              </Form.Item>

更新 #4

果然,删除 Form 标签中的 name 属性解决了上述问题,它按预期工作,没有错误。

为了结束这个问题,希望它可以帮助其他人,我想捕捉解决方案。最终,解决方案相当于使当用户跳出相关字段时,该字段的值被设置为用户键入的内容。这是通过将 onBlur 标签添加到我的 antd Select 标签来完成的。 onBlur 在用户跳出字段时触发。然后将字段设置为输入的字符。由于我使用了动态表单和嵌套对象数组(申请者),结果比我想象的要复杂。

请注意,状态现在显示在 value 下,并且名称路径与代码所期望的匹配。

到目前为止,我的最终代码如下所示

import React from "react";
import { Form, Input, Col, Row, Select, Button } from "antd";
import { PlusOutlined, MinusCircleTwoTone } from "@ant-design/icons";

const InputGroup = Input.Group;
const Option = Select.Option;
const { TextArea } = Input;
const maxApplicants = 4;

const rules = [{ required: true }];

const STATES = [
  "AL",
  "AK",
  "AZ",
  "AR",
  "CA",
  "CO",
  "CT",
  "DE",
  "DC",
  "FM",
  "FL",
  "GA",
  "GU",
  "HI",
  "ID",
  "IL",
  "IN",
  "IA",
  "KS",
  "KY",
  "LA",
  "ME",
  "MH",
  "MD",
  "MA",
  "MI",
  "MN",
  "MS",
  "MO",
  "MT",
  "NE",
  "NV",
  "NH",
  "NJ",
  "NM",
  "NY",
  "NC",
  "ND",
  "MP",
  "OH",
  "OK",
  "OR",
  "PW",
  "PA",
  "PR",
  "RI",
  "SC",
  "SD",
  "TN",
  "TX",
  "UT",
  "VT",
  "VI",
  "VA",
  "WA",
  "WV",
  "WI",
  "WY",
];

const validateMessages = {
  required: "Required!",
  types: {
    email: "Invalid E-mail!",
    number: "Invalid Number!",
  },
  number: {
    range: "Must be between ${min} and ${max}",
  },
};

const formItemLayout = {
  labelCol: {
    xs: { span: 24 },
    sm: { span: 6 },
  },
  wrapperCol: {
    xs: { span: 36 },
    sm: { span: 16 },
  },
};

const formItemLayoutWithOutLabel = {
  wrapperCol: {
    xs: { span: 36, offset: 0 },
    sm: { span: 16, offset: 6 },
  },
};

export default class TenantForm extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      applicants: [
        {
          firstName: props.tenant ? props.tenant.first : "",
          middleName: props.tenant ? props.tenant.middle : "",
          lastName: props.tenant ? props.tenant.last : "",
          street: props.tenant ? props.tenant.street : "",
          city: props.tenant ? props.tenant.city : "",
          state: props.tenant ? props.tenant.state : "",
          zipcode: props.tenant ? props.tenant.zipcode : "",
        },
      ],
      error: "",
    };
  }

  formRef = React.createRef();

  render() {
    const onFinish = (values) => {
      console.log("onFinish: ", values);
      this.props.onSubmit({
        applicants: values.applicants,
      });
    };

    return (
      <Form
        ref={this.formRef}
        {...formItemLayoutWithOutLabel}
        onFinish={onFinish}
        id="myForm"
        validateMessages={validateMessages}
        initialValues={{ applicants: [{ firstName: "" }] }}
      >
        {
          <Form.List name="applicants">
            {(fields, { add, remove }) => {
              return (
                <div>
                  {fields.map((field, index) => (
                    <Form.Item {...formItemLayout} label={`Applicant #${index + 1}`} key={field.key}>
                      <Row key={field.key} gutter={[0, 8]} justify="start">
                        <Col>
                          <Row gutter={[4, 4]}>
                            <Col span={7}>
                              <Form.Item name={[field.name, "firstName"]} fieldKey={[field.fieldKey, "firstName"]} rules={rules}>
                                <Input placeholder="First Name" />
                              </Form.Item>
                            </Col>
                            <Col span={7}>
                              <Form.Item name={[field.name, "middleName"]} fieldKey={[field.fieldKey, "middleName"]} initialValue="">
                                <Input placeholder="Middle Name" />
                              </Form.Item>
                            </Col>
                            <Col span={9}>
                              <Form.Item name={[field.name, "lastName"]} fieldKey={[field.fieldKey, "lastName"]} rules={rules}>
                                <Input placeholder="Last Name" />
                              </Form.Item>
                            </Col>
                            <Col flex="none">
                              {index > 0 ? (
                                <MinusCircleTwoTone
                                  className="dynamic-delete-button"
                                  onClick={() => {
                                    remove(field.name);
                                  }}
                                />
                              ) : null}
                            </Col>
                          </Row>
                          <Row gutter={[4, 4]}>
                            <Col span={23}>
                              <Form.Item name={[field.name, "address"]} fieldKey={[field.fieldKey, "address"]} rules={rules}>
                                <Input placeholder="Address" />
                              </Form.Item>
                            </Col>
                          </Row>
                          <Row gutter={[4, 4]}>
                            <Col span={12}>
                              <Form.Item name={[field.name, "city"]} fieldKey={[field.fieldKey, "city"]} rules={rules}>
                                <Input placeholder="City" />
                              </Form.Item>
                            </Col>
                            <Col span={5}>
                              <Form.Item name={[field.name, "state"]} fieldKey={[field.fieldKey, "state"]} rules={rules}>
                                <Select
                                  showSearch
                                  placeholder="State"
                                  defaultActiveFirstOption={true}
                                  onBlur={(event) => {
                                    console.log("values", this.formRef.current.getFieldsValue());
                                    console.log("target:", event.target);
                                    console.log("id", event.target.id);
                                    const stateIndex = event.target.getAttribute("aria-activedescendant").split("_").slice(-1);
                                    const values = this.formRef.current.getFieldsValue();
                                    const namePath = event.target.id.split("_");
                                    console.log("namePath", namePath);
                                    values[namePath[0]][namePath[1]][namePath[2]] = event.target.value.toUpperCase() || STATES[stateIndex];
                                    this.formRef.current.setFieldsValue(values);
                                  }}
                                >
                                  {STATES.map((state) => (
                                    <Option value={state} key={state}>
                                      {state}
                                    </Option>
                                  ))}
                                </Select>
                              </Form.Item>
                            </Col>
                            <Col span={6}>
                              <Form.Item name={[field.name, "zipcode"]} fieldKey={[field.fieldKey, "zipcode"]} rules={rules}>
                                <Input placeholder="Zip Code" />
                              </Form.Item>
                            </Col>
                          </Row>
                        </Col>
                      </Row>
                    </Form.Item>
                  ))}
                  <Form.Item>
                    {console.log("count:", fields.length)}
                    {fields.length < maxApplicants ? (
                      <Button
                        type="dashed"
                        onClick={() => {
                          add();
                        }}
                        style={{ width: "100%" }}
                      >
                        <PlusOutlined /> Add Applicant
                      </Button>
                    ) : null}
                  </Form.Item>
                </div>
              );
            }}
          </Form.List>
        }
      </Form>
    );
  }
}

【问题讨论】:

    标签: javascript reactjs antd


    【解决方案1】:

    诀窍是在 Select 组件上使用 onBlurform.setFieldsValue

    请参阅下面的代码框: https://codesandbox.io/embed/distracted-platform-pvdiz?fontsize=14&hidenavigation=1&theme=dark

    【讨论】:

    • 它在类组件中的工作方式相同,但您可以通过参考访问表单实例:formRef = React.createRef(); 请参见此处的第 24 行和第 49 行:codesandbox.io/s/xwqju?file=/index.js:391-422 如果我的回答有帮助您,如果您能将我的回答标记为有用,我将不胜感激。干杯!
    • 在您的 onFinish 处理程序中,您能否 console.log values 并发布结果。我相信state 字段没有正确设置,因为state 不是values 对象内的顶级属性。这个name={[field.name, "state"]} 似乎表明state 嵌套在一个评估为field.name 的属性内。
    • 动态表单的更新解决方案:codesandbox.io/s/nervous-wilson-tr5wx?file=/index.js
    • 能否包含包含FormForm.List 组件的代码块?看起来您可能在两个组件中都定义了 name 属性,并且因为 name 值有下划线,所以我的 split 方法无法正常工作。是的。这变得多么复杂真是太疯狂了,但幸运的是,一旦你确保了你想要的行为,你可以将组件包装在一个 HOC 中,以使该行为在未来的组件中可重用。
    • 顺便说一句,我认为如果您删除了 name 组件上的 name 道具,那么您目前实施我的方法的尝试会奏效。如果您打算通过应用程序中的其他地方的Form.Provider 访问表单实例,我很确定您只需要给Form 提供一个name 属性。
    猜你喜欢
    • 1970-01-01
    • 2015-08-01
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 2020-07-26
    • 2011-06-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多