Antd Select 表单项键盘行为问题 [英] Antd Select form item keyboard behavior problem

查看:51
本文介绍了Antd Select 表单项键盘行为问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

无论如何,这似乎是一个相当简单的问题,但我无法找到解决方案.

选择表单项的代码:

 <Form.Item name={[field.name, "state"]} fieldKey={[field.fieldKey, "state"]} rules={规则}><选择显示搜索占位符=状态"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></Form.Item>

更新 #1

所以按照评论中的建议(我注意到类似的方法

更新 #3

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

 {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 值 = 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() ||状态[状态索引];this.formRef.current.setFieldsValue(values);}}>{STATES.map((状态) => (<选项值={state} key={state}>{状态}</选项>))}</Form.Item>

更新 #4

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

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

请注意,状态现在显示在 value 下,并且 namepath 与代码期望的匹配.

到目前为止我的最终代码是这样的

从react"导入React;import { Form, Input, Col, Row, Select, Button } from "antd";从@ant-design/icons"导入 { PlusOutlined, MinusCircleTwoTone };const InputGroup = Input.Group;const 选项 = Select.Option;const { TextArea } = 输入;const maxApplicants = 4;const 规则 = [{ required: true }];常量状态 = ["AL","AK","AZ","AR","CA","CO","CT","德",直流",调频","FL","GA",顾",你好",ID","伊尔",在","IA","KS","KY",洛杉矶",我","MH","MD",嘛","米","MN",多发性硬化症","莫",公吨","NE","NV","NH",新泽西","NM","纽约","NC","ND","议员",哦",好的",或者","PW","PA",公关","RI","SC","SD","TN","TX","UT","VT",六","VA","哇","WV","威","WY",];常量验证消息 = {要求:必须!",类型:{email: "无效的电子邮件!",number: "无效号码!",},数字: {范围:必须在 ${min} 和 ${max} 之间",},};const formItemLayout = {标签列:{xs: { 跨度: 24 },sm: { 跨度: 6 },},包装颜色:{xs: { 跨度: 36 },sm: { 跨度: 16 },},};const formItemLayoutWithOutLabel = {包装颜色:{xs: { 跨度: 36, 偏移量: 0 },sm: { 跨度: 16, 偏移量: 6 },},};导出默认类 TenantForm 扩展 React.Component {构造函数(道具){超级(道具);this.state = {申请人:[{名字: props.tenant ?props.tenant.first : "",中间名: props.tenant ?props.tenant.middle : "",姓氏: props.tenant ?props.tenant.last : "",街道: props.tenant ?props.tenant.street : "",城市:道具.租户?props.tenant.city : "",状态: props.tenant ?props.tenant.state : "",邮政编码: props.tenant ?props.tenant.zipcode : "",},],错误: "",};}formRef = React.createRef();使成为() {const onFinish = (values) =>{console.log("onFinish: ", values);this.props.onSubmit({申请人:values.applicants,});};返回 (<表格ref={this.formRef}{...formItemLayoutWithOutLabel}onFinish={onFinish}id="myForm"validateMessages={validateMessages}initialValues={{ 申请人: [{ firstName: "" }] }}>{<Form.List name="申请人">{(字段,{添加,删除})=>{返回 (<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><行间距={[4, 4]}><Col span={7}><Form.Item name={[field.name, "firstName"]} fieldKey={[field.fieldKey, "firstName"]} rules={rules}><输入占位符=名字"/></Form.Item></Col><Col span={7}><Form.Item name={[field.name, "middleName"]} fieldKey={[field.fieldKey, "middleName"]} initialValue=""><输入占位符="中间名"/></Form.Item></Col><Col span={9}><Form.Item name={[field.name, "lastName"]} fieldKey={[field.fieldKey, "lastName"]} rules={rules}><输入占位符=姓氏"/></Form.Item></Col><Col flex="无">{索引>0 ?(<减号CircleTwoToneclassName="动态删除按钮"onClick={() =>{删除(字段名称);}}/>) : 空值}</Col></行><行间距={[4, 4]}><Col span={23}><Form.Item name={[field.name, "address"]} fieldKey={[field.fieldKey, "address"]} rules={rules}><输入占位符=地址"/></Form.Item></Col></行><行间距={[4, 4]}><Col span={12}><Form.Item name={[field.name, "city"]} fieldKey={[field.fieldKey, "city"]} rules={rules}><输入占位符=城市"/></Form.Item></Col><Col span={5}><Form.Item name={[field.name, "state"]} fieldKey={[field.fieldKey, "state"]} rules={rules}><选择显示搜索占位符=状态"defaultActiveFirstOption={true}onBlur={(事件)=>{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 值 = 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() ||状态[状态索引];this.formRef.current.setFieldsValue(values);}}>{STATES.map((状态) => (<选项值={state} key={state}>{状态}</选项>))}</Form.Item></Col><Col span={6}><Form.Item name={[field.name, "zipcode"]} fieldKey={[field.fieldKey, "zipcode"]} rules={rules}><输入占位符=邮政编码"/></Form.Item></Col></行></Col></行></Form.Item>))}<表单.项目>{console.log("count:", fields.length)}{fields.length <最大申请人?(<按钮类型=虚线"onClick={() =>{添加();}}样式={{ 宽度:100%"}}><PlusOutlined/>添加申请人</按钮>) : 空值}</Form.Item>

);}}</Form.List>}</表格>);}}

解决方案

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

见下面的代码和框:https://codesandbox.io/embed/分心-平台-pvdiz?fontsize=14&hidenavigation=1&theme=dark

I have a form which for the most part works fine. But one thing bugs me about it is the Select item for imputing a person's state (see screenshot below). It works fine with the mouse, but if you're tabbing through the form and tab to the State field, then type a state like "CA" for California, then tab to the next field (zipcode), the State field resets to blank. I would like it to stay populated with CA if it matched one of the selector's options.

It does work if i type "CA" and hit enter to advance to the next field, but that doesn't seem intuitive as to how forms normally work. It also works if I just use my mouse to select the field. I've been looking over the antd documentation on a Select and don't see anything jumping out at me that might control this. Maybe i'm missing something and i need to do some kind of onchange function?

Anyways, seems like a fairly simple problem but the solution is eluding me.

The select form item's code:

                              <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>

Update #1

So following the suggestions in the comments (and my noticing a similar approach here under the form control section), to my class i've added a formRef = React.createRef();.

Then in my Form tag I added a ref={this.formRef} tag. Then modified my Select by adding an onBlur tag that triggers setFieldsValue to set the uppercase version of the input.

This compiles and runs fine. It even outputs the console.log statement. But it does not set the field value for some reason and i'm not sure why.

    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() });
**                                }}
                                >

Update #2

screenshot of values when onFinish is called. Note this is a dynamic form where user can add additional applicants by clicking an add applicant button. Each applicant has the fields shown including the state they live in.

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

Update #3

Tried to mimic the approach however for some reason in the object being returned by values, state is absent and ultimately i'm getting an uncaught typeerror and state doesn't populate.

                              <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>

Update #4

So sure enough, removal of the name property in the Form tag resolved the above issue and it's working as expected without error.

In the interest of wrapping up this question in hopes it may help someone else, I wanted to capture the solution. Ultimately the solution amounted to making it so when the user tabs out of the field in question, the field's value gets set to what the user typed. This was done by adding the onBlur tag to my antd Select tag. onBlur fires when the user tabs out of the field. Then making it set the field to the characters entered. Which turned out to be more complicated then I would've thought due to my use of a dyanmic form and nested object array (applicants).

Note the state now shows up under value and the namepath matches what the code expects.

My final code up until this point looks like this

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>
    );
  }
}

解决方案

The trick is use onBlur on the Select component in combination with form.setFieldsValue.

See codesandbox below: https://codesandbox.io/embed/distracted-platform-pvdiz?fontsize=14&hidenavigation=1&theme=dark

这篇关于Antd Select 表单项键盘行为问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆