【发布时间】: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