I'm using antd and I wanna use its registration Form in my project.
Note: Before adding this component my project (registration.js) was working correctly and I've just copied and pasted the antd component.
Here is my registration.js file:
import { Form, Input, Tooltip, Icon, Cascader, Select, Row, Col, Checkbox, Button, AutoComplete } from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;
const AutoCompleteOption = AutoComplete.Option;
class RegistrationForm extends React.Component {
state = {
confirmDirty: false,
autoCompleteResult: [],
};
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
}
handleConfirmBlur = (e) => {
const value = e.target.value;
this.setState({ confirmDirty: this.state.confirmDirty || !!value });
}
compareToFirstPassword = (rule, value, callback) => {
const form = this.props.form;
if (value && value !== form.getFieldValue('password')) {
callback('Two passwords that you enter is inconsistent!');
} else {
callback();
}
}
validateToNextPassword = (rule, value, callback) => {
const form = this.props.form;
if (value && this.state.confirmDirty) {
form.validateFields(['confirm'], { force: true });
}
callback();
}
handleWebsiteChange = (value) => {
let autoCompleteResult;
if (!value) {
autoCompleteResult = [];
} else {
autoCompleteResult = ['.com', '.org', '.net'].map(domain => `${value}${domain}`);
}
this.setState({ autoCompleteResult });
}
render() {
const { getFieldDecorator } = this.props.form;
const { autoCompleteResult } = this.state;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
};
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 24,
offset: 0,
},
sm: {
span: 16,
offset: 8,
},
},
};
const prefixSelector = getFieldDecorator('prefix', {
initialValue: '86',
})(
<Select style={{ width: 70 }}>
<Option value="86">+86</Option>
<Option value="87">+87</Option>
</Select>
);
const websiteOptions = autoCompleteResult.map(website => (
<AutoCompleteOption key={website}>{website}</AutoCompleteOption>
));
return (
<Form onSubmit={this.handleSubmit}>
<FormItem
{...formItemLayout}
label="E-mail"
>
{getFieldDecorator('email', {
rules: [{
type: 'email', message: 'The input is not valid E-mail!',
}, {
required: true, message: 'Please input your E-mail!',
}],
})(
<Input />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Password"
>
{getFieldDecorator('password', {
rules: [{
required: true, message: 'Please input your password!',
}, {
validator: this.validateToNextPassword,
}],
})(
<Input type="password" />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Confirm Password"
>
{getFieldDecorator('confirm', {
rules: [{
required: true, message: 'Please confirm your password!',
}, {
validator: this.compareToFirstPassword,
}],
})(
<Input type="password" onBlur={this.handleConfirmBlur} />
)}
</FormItem>
<FormItem
{...formItemLayout}
label={(
<span>
Nickname
<Tooltip title="What do you want others to call you?">
<Icon type="question-circle-o" />
</Tooltip>
</span>
)}
>
{getFieldDecorator('nickname', {
rules: [{ required: true, message: 'Please input your nickname!', whitespace: true }],
})(
<Input />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Habitual Residence"
>
{getFieldDecorator('residence', {
initialValue: ['zhejiang', 'hangzhou', 'xihu'],
rules: [{ type: 'array', required: true, message: 'Please select your habitual residence!' }],
})(
<Cascader options={residences} />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Phone Number"
>
{getFieldDecorator('phone', {
rules: [{ required: true, message: 'Please input your phone number!' }],
})(
<Input addonBefore={prefixSelector} style={{ width: '100%' }} />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Website"
>
{getFieldDecorator('website', {
rules: [{ required: true, message: 'Please input website!' }],
})(
<AutoComplete
dataSource={websiteOptions}
onChange={this.handleWebsiteChange}
placeholder="website"
>
<Input />
</AutoComplete>
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Captcha"
extra="We must make sure that your are a human."
>
<Row gutter={8}>
<Col span={12}>
{getFieldDecorator('captcha', {
rules: [{ required: true, message: 'Please input the captcha you got!' }],
})(
<Input />
)}
</Col>
<Col span={12}>
<Button>Get captcha</Button>
</Col>
</Row>
</FormItem>
<FormItem {...tailFormItemLayout}>
{getFieldDecorator('agreement', {
valuePropName: 'checked',
})(
<Checkbox>I have read the agreement</Checkbox>
)}
</FormItem>
<FormItem {...tailFormItemLayout}>
<Button type="primary" htmlType="submit">Register</Button>
</FormItem>
</Form>
);
}
}
const WrappedRegistrationForm = Form.create()(RegistrationForm);
ReactDOM.render(<WrappedRegistrationForm />, document.getElementById('main'));
And this is my index.html file:
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>React Project</title>
</head>
<body>
<div id="main"></div>
<script src="/build/bundle.js"></script>
</body>
</html>
I face with this error in my registration.js file:
Unexpected token (8:8) while parsing file
Line 8 refers to where we have state = {, how can I solve this problem?
Probably you are not using the right transpilers.
Declaring global variables in classes like your state = { ... } is only possible with ES7 (I believe). You are probably using a ES6 transpiler.
One fix would be changing your syntax to ES6. So you would have to declare you state like this:
class RegistrationForm extends React.Component {
constructor(props) {
super(props);
this.state = { ... };
}
// ...
}
ES6 also doesn't support declaring class methods with arrow syntax (myMethod = () => { ... }). You would have to change all of your methods to myMethod() { ... }
Another fix would be to download the right presets so babel (I'm guessing you are using babel) can transpile your ES7 syntax.
Related
I have a form with ant design. I want to add a rule for each input field that the user can't enter spaces to fill the input. (spaces forbidden)
I try this method { transform: (value) => value.trim() }but it doesn't work.
I appreciate your help.
<>
<Form.Item
label={t("forms.inputs.Name.label")}
rules={[
{
required: true,
message: t("forms.inputs.Name.rules.required"),
},
{
min: 3,
message: t("forms.inputs.Name.rules.minLength"),
},
]}>
<Input />
</Form.Item>
<Form.Item
label={t("forms.inputs.job.label")}
rules={[
{
required: true,
message: t("forms.inputs.job.rules.required"),
},
]}>
<Input />
</Form.Item>
<Form.Item
label={t("forms.inputs.Company.label")}
rules={[
{
required: true,
message: t("forms.inputs.Company.rules.required"),
},
]}>
<Input placeholder={t("forms.inputs.currentCompany.placeholder")} />
</Form.Item>
</>
Just write a custom validation rule:
<Form.Item
label="Username"
name="username"
rules={[
{
required: true,
message: "Required"
},
{
validator: (_, value) =>
!value.includes(" ")
? Promise.resolve()
: Promise.reject(new Error("No spaces allowed"))
}
]}
>
<Input />
</Form.Item>
For email validation, you can use the following regex pattern:
<Form.Item
label="Email"
name="email"
rules={[
{
required: true,
message: "Required"
},
{
pattern: /([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|\"([]!#-[^-~ \t]|(\\[\t -~]))+\")#([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|\[[\t -Z^-~]*])/,
message: "Invalid email"
}
]}
normalize={(value, prevVal, prevVals) => value.trim()}
>
<Input />
</Form.Item>
DEMO
Instead of trimming onChange, do that in an onBlur callback:
formatInput = (event) => { const attribute = event.target.getAttribute('name') this.setState({ [attribute]: event.target.value.trim() }) }
or when you click enter
onKeyPress={(e) => {if (e.key === "Enter") {setValue(e.target.value.trim())} }}
you can do like this instead of using trim()
import React, { useState } from 'react';
import 'antd/dist/antd.css';
import { Form, Input } from 'antd';
const App = () => {
const [inputValue, setValue] = useState({input1: '', input2: '', input3: ''});
const onFinish = (values) => {
console.log('Success:', values);
};
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo);
};
const handleOnChange = (e) => {
let {value} = e.target;
let data = {...inputValue};
if (value.length && value[0] === ' ') {
data[e.target.name] = '';
setValue(data);
return;
}
data[e.target.name] = value;
setValue(data);
}
return (
<Form
name="basic"
labelCol={{
span: 8,
}}
wrapperCol={{
span: 16,
}}
initialValues={{
remember: true,
}}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
>
<>
<Form.Item
label={t("forms.inputs.Name.label")}
rules={[
{
required: true,
message: t("forms.inputs.Name.rules.required"),
},
{
min: 3,
message: t("forms.inputs.Name.rules.minLength"),
},
]}>
<Input name="input1" value={inputValue.input1} onChange={handleOnChange} />
</Form.Item>
<Form.Item
label={t("forms.inputs.job.label")}
rules={[
{
required: true,
message: t("forms.inputs.job.rules.required"),
},
]}>
<Input name="input2" value={inputValue.input2} onChange={handleOnChange} />
</Form.Item>
<Form.Item
label={t("forms.inputs.Company.label")}
rules={[
{
required: true,
message: t("forms.inputs.Company.rules.required"),
},
]}>
<Input name="input3" value={inputValue.input3} onChange={handleOnChange} placeholder={t("forms.inputs.currentCompany.placeholder")} />
</Form.Item>
</>
</Form>
);
};
export default App;
I created a model for a form which maps throught it to create form using react-hook-form
when i submit form for the first time, everything is Okay. but for the other times, when i paste a value into inputs, validation not work correctly(it shows required error but the input is not empty)
this is model:
export const formData = [
{
id: 1,
type: "text",
labelRequired: true,
label: "name",
shape: "inline",
placeholder: "name",
name: "nameOne",
validation: {
required: true,
minLength: 8,
},
size: 6,
},
{
id: 2,
type: "text",
labelRequired: true,
label: "family",
shape: "inline",
placeholder: "family",
name: "nameTwo",
validation: {
required: true,
minLength: 8,
},
size: 6,
},
{
id: 7,
type: "checkbox",
label: "Sample",
shape: "checkbox",
placeholder: "placeholder",
name: "checkbox_btn",
data: [
{
id: 41,
inputId: "inline-form-1",
label: "1",
},
],
size: 12,
},
];
the map method to create form based on model:
const Sample = () => {
const {
register,
handleSubmit,
formState: { errors },
reset,
} = useForm({
mode: "onBlur",
});
const submitHandler = (data, e) => {
e.target.reset();
console.log(data);
reset();
};
const mapForm = formData.map((item) => {
if (item.shape === "inline") {
return (
<InputContainer
key={item.id}
register={{
...register(item.name, {
required: item.validation.required,
minLength: item.validation.minLength,
}),
}}
validation={item.validation}
placeholder={item.placeholder}
label={item.label}
size={item.size}
error={errors[item.name]}
/>
);
}
if (item.shape === "checkbox") {
return (
<CheckBoxContainer
key={item.id}
label={item.label}
data={item.data}
register={{ ...register("medium", { required: true }) }}
error={errors.medium}
/>
);
}
});
return (
<Fragment>
<Breadcrumb parent="Dashboard" title="Default" />
<Container fluid={true}>
<Row>
<Col sm="12">
<Card>
<CardHeader>
<h5>Sample Card</h5>
</CardHeader>
<CardBody>
<Form onSubmit={handleSubmit(submitHandler)}>
<Row>{mapForm}</Row>
<Button type="submit" className="m-t-40">
Submit
</Button>
</Form>
</CardBody>
</Card>
</Col>
</Row>
</Container>
</Fragment>
);
};
export default Sample;
Also there are some container components for wrapping inputs like:
const InputContainer = ({
id,
register,
error,
label,
labelRequired,
size,
type,
placeholder,
validation,
}) => {
return (
<Col lg={size} style={{ marginTop: "-10px" }}>
<FormGroup>
<Label className={`col-form-label`}>
{label} {labelRequired && <span></span>}
</Label>
<Input
className="form-control"
type={type}
placeholder={placeholder}
{...register}
/>
{error && error.type === "required" && (
<p className="p-16 text-danger">this filed is required</p>
)}
</FormGroup>
</Col>
);
};
export default InputContainer;
I have not looked that deep into it to find out if this happens from a simple misuse of the useForm() hook, but here's what I've figured out.
According to the source code, register() returns the following interface (unrelated stuff omitted):
type UseFormRegisterReturn<...> = {
onChange: ChangeHandler;
onBlur: ChangeHandler;
ref: RefCallBack;
...
}
As you can see, this means only the onChange() and onBlur() events are being registered. onChange() is the one we're interested in.
After some quick testing, I've realized that for some unknown reason (maybe a browser bug? no idea), in certain conditions onChange() doesn't trigger when pasting text with CTRL+V.
Luckily, the regular onInput() event still triggers, so we can simply define this event using the register.onChange() handler:
<Input
className="form-control"
type={type}
placeholder={placeholder}
{...register}
onInput={register && register.onChange}
// TypeScript
// onInput={register?.onChange}
/>
If onInput() is already being used:
<Input
className="form-control"
type={type}
placeholder={placeholder}
{...register}
onInput={e => {
doOtherStuff(e);
if (register && register.onChange) {
register.onChange(e);
}
// TypeScript
// register?.onChange(e);
}
/>
Using element-react UI components, I've been struggling to solve an issue caused by wrapping a form in
<UserContext.Consumer>
</UserContext.Consumer>
I get the error message:
Error: Function components cannot have string refs. We recommend using useRef() instead.
The content of the class is a copy/paste from here, under the heading of validation. And this code works fine in my application if I don't use the UserContext piece.
UserContext is declared in App.js as
export const UserContext = React.createContext();
Can you tell me how to rewrite this so I can use UserContext and have the fields validate with element-react please.
import React, { Component } from 'react';
import { i18n, Dialog, Button, Notification, Radio, Layout, DatePicker, Form,
Select,Input,TimePicker,Switch,Checkbox } from "element-react";
import { UserContext } from '../App';
class MyPetsPage extends Component {
constructor(props) {
super(props);
this.state = {
form: {
name: '',
region: '',
date1: null,
date2: null,
delivery: false,
type: [],
resource: '',
desc: ''
},
rules: {
name: [
{ required: true, message: 'Please input Activity name', trigger: 'blur' }
],
region: [
{ required: true, message: 'Please select Activity zone', trigger: 'change' }
],
date1: [
{ type: 'date', required: true, message: 'Please pick a date', trigger: 'change' }
],
date2: [
{ type: 'date', required: true, message: 'Please pick a time', trigger: 'change' }
],
type: [
{ type: 'array', required: true, message: 'Please select at least one activity type', trigger:
'change' }
],
resource: [
{ required: true, message: 'Please select activity resource', trigger: 'change' }
],
desc: [
{ required: true, message: 'Please input activity form', trigger: 'blur' }
]
}
};
}
handleSubmit(e) {
e.preventDefault();
this.refs.form.validate((valid) => {
if (valid) {
alert('submit!');
} else {
console.log('error submit!!');
return false;
}
});
}
handleReset(e) {
e.preventDefault();
this.refs.form.resetFields();
}
onChange(key, value) {
this.setState({
form: Object.assign({}, this.state.form, { [key]: value })
});
}
render() {
return ( <UserContext.Consumer>
{({user}) => <>
<Form ref="form" className="en-US" model={this.state.form} rules={this.state.rules}
labelWidth="120">
<Form.Item label="Activity name" prop="name">
<Input value={this.state.form.name} onChange={this.onChange.bind(this, 'name')}></Input>
</Form.Item>
<Form.Item label="Activity zone" prop="region">
<Select value={this.state.form.region} placeholder="Activity zone" onChange=
{this.onChange.bind(this, 'region')}>
<Select.Option label="Zone 1" value="shanghai"></Select.Option>
<Select.Option label="Zone 2" value="beijing"></Select.Option>
</Select>
</Form.Item>
<Form.Item label="Activity time" required={true}>
<Layout.Col span="11">
<Form.Item prop="date1" labelWidth="0px">
<DatePicker
value={this.state.form.date1}
placeholder="Pick a date"
onChange={this.onChange.bind(this, 'date1')}
/>
</Form.Item>
</Layout.Col>
<Layout.Col className="line" span="2">-</Layout.Col>
<Layout.Col span="11">
<Form.Item prop="date2" labelWidth="0px">
<TimePicker
value={this.state.form.date2}
selectableRange="18:30:00 - 20:30:00"
placeholder="Pick a time"
onChange={this.onChange.bind(this, 'date2')}
/>
</Form.Item>
</Layout.Col>
</Form.Item>
<Form.Item label="Instant delivery" prop="delivery">
<Switch value={this.state.form.delivery} onChange={this.onChange.bind(this, 'delivery')}>
</Switch>
</Form.Item>
<Form.Item label="Activity type" prop="type">
<Checkbox.Group value={this.state.form.type} onChange={this.onChange.bind(this, 'type')}>
<Checkbox label="Online activities" name="type"></Checkbox>
<Checkbox label="Promotion activities" name="type"></Checkbox>
<Checkbox label="Offline activities" name="type"></Checkbox>
<Checkbox label="Simple brand exposure" name="type"></Checkbox>
</Checkbox.Group>
</Form.Item>
<Form.Item label="Resources" prop="resource">
<Radio.Group value={this.state.form.resource} onChange={this.onChange.bind(this, 'resource')}>
<Radio value="Sponsor"></Radio>
<Radio value="Venue"></Radio>
</Radio.Group>
</Form.Item>
<Form.Item label="Activity form" prop="desc">
<Input type="textarea" value={this.state.form.desc} onChange={this.onChange.bind(this, 'desc')}>
</Input>
</Form.Item>
<Form.Item>
<Button type="primary" onClick={this.handleSubmit.bind(this)}>Create</Button>
<Button onClick={this.handleReset.bind(this)}>Reset</Button>
</Form.Item>
</Form>
</>} </UserContext.Consumer>
)
}
}
export default MyPetsPage;
Like the error message is telling you:
Function components cannot have string refs
Since you've made a class-based component, you can use React.createRef to create your ref:
constructor(props) {
super(props);
// ...
this.form = React.createRef();
}
Now instead of this:
<Form ref="form"> // ...
do this:
<Form ref={this.form}> // ...
Now you can call the validate function using your form ref like this:
this.form.current.validate(...)
Complete example:
import React, { Component } from "react";
import { useState, useRef, useCallback } from "react";
import {
i18n,
Dialog,
Button,
Notification,
Radio,
Layout,
DatePicker,
Form,
Select,
Input,
TimePicker,
Switch,
Checkbox,
} from "element-react";
import "element-theme-default";
const UserContext = React.createContext("user");
class MyPetsPage extends Component {
constructor(props) {
super(props);
this.state = {
form: {
name: "",
region: "",
date1: null,
date2: null,
delivery: false,
type: [],
resource: "",
desc: "",
},
rules: {
name: [
{
required: true,
message: "Please input Activity name",
trigger: "blur",
},
],
region: [
{
required: true,
message: "Please select Activity zone",
trigger: "change",
},
],
date1: [
{
type: "date",
required: true,
message: "Please pick a date",
trigger: "change",
},
],
date2: [
{
type: "date",
required: true,
message: "Please pick a time",
trigger: "change",
},
],
type: [
{
type: "array",
required: true,
message: "Please select at least one activity type",
trigger: "change",
},
],
resource: [
{
required: true,
message: "Please select activity resource",
trigger: "change",
},
],
desc: [
{
required: true,
message: "Please input activity form",
trigger: "blur",
},
],
},
};
this.form = React.createRef();
}
handleSubmit(e) {
e.preventDefault();
this.form.current.validate((valid) => {
if (valid) {
alert("submit!");
} else {
console.log("error submit!!");
return false;
}
});
}
handleReset(e) {
e.preventDefault();
this.form.current.resetFields();
}
onChange(key, value) {
this.setState({
form: Object.assign({}, this.state.form, { [key]: value }),
});
}
render() {
return (
<UserContext.Consumer>
{(value) => (
<>
<Form
ref={this.form}
className="en-US"
model={this.state.form}
rules={this.state.rules}
labelWidth="120"
>
<Form.Item label="Activity name" prop="name">
<Input
value={this.state.form.name}
onChange={this.onChange.bind(this, "name")}
></Input>
</Form.Item>
<Form.Item label="Activity zone" prop="region">
<Select
value={this.state.form.region}
placeholder="Activity zone"
onChange={this.onChange.bind(this, "region")}
>
<Select.Option
label="Zone 1"
value="shanghai"
></Select.Option>
<Select.Option label="Zone 2" value="beijing"></Select.Option>
</Select>
</Form.Item>
<Form.Item label="Activity time" required={true}>
<Layout.Col span="11">
<Form.Item prop="date1" labelWidth="0px">
<DatePicker
value={this.state.form.date1}
placeholder="Pick a date"
onChange={this.onChange.bind(this, "date1")}
/>
</Form.Item>
</Layout.Col>
<Layout.Col className="line" span="2">
-
</Layout.Col>
<Layout.Col span="11">
<Form.Item prop="date2" labelWidth="0px">
<TimePicker
value={this.state.form.date2}
selectableRange="18:30:00 - 20:30:00"
placeholder="Pick a time"
onChange={this.onChange.bind(this, "date2")}
/>
</Form.Item>
</Layout.Col>
</Form.Item>
<Form.Item label="Instant delivery" prop="delivery">
<Switch
value={this.state.form.delivery}
onChange={this.onChange.bind(this, "delivery")}
></Switch>
</Form.Item>
<Form.Item label="Activity type" prop="type">
<Checkbox.Group
value={this.state.form.type}
onChange={this.onChange.bind(this, "type")}
>
<Checkbox label="Online activities" name="type"></Checkbox>
<Checkbox label="Promotion activities" name="type"></Checkbox>
<Checkbox label="Offline activities" name="type"></Checkbox>
<Checkbox
label="Simple brand exposure"
name="type"
></Checkbox>
</Checkbox.Group>
</Form.Item>
<Form.Item label="Resources" prop="resource">
<Radio.Group
value={this.state.form.resource}
onChange={this.onChange.bind(this, "resource")}
>
<Radio value="Sponsor"></Radio>
<Radio value="Venue"></Radio>
</Radio.Group>
</Form.Item>
<Form.Item label="Activity form" prop="desc">
<Input
type="textarea"
value={this.state.form.desc}
onChange={this.onChange.bind(this, "desc")}
></Input>
</Form.Item>
<Form.Item>
<Button type="primary" onClick={this.handleSubmit.bind(this)}>
Create
</Button>
<Button onClick={this.handleReset.bind(this)}>Reset</Button>
</Form.Item>
</Form>
</>
)}
</UserContext.Consumer>
);
}
}
const App = () => {
return (
<div className="App">
<UserContext.Provider value="user">
<MyPetsPage />
</UserContext.Provider>
</div>
);
};
At this point it will work, but you will still see errors in the console about string refs. This is because element-react actually also uses string-refs.
I have long registration form which I have divided into parts using steps component of AntD.
// Defined Child Form Components
steps = {
basicInfo: {
title: 'Basic Info',
content: (
<BasicInfoForm
form={this.props.form}
user={this.user}
/>
),
},
addresses: {
title: 'Addresses',
content: (
<Address
form={this.props.form}
addresses={this.user.addresses}
/>
),
},
contactInfo: {
title: 'Contact Info',
content: (
<PhoneForm
form={this.props.form}
contactInfo={this.user.contactInfo}
/>
),
},
}
// Form configuring these child form components
<Form onSubmit={handleSubmit} className="add-edit-user-form">
<Fragment>
<Steps progressDot current={this.state.currentStep}>
{Object.keys(steps).map((key, index) => {
return <Step title={steps && steps[index] ? steps[index].title : null} key={index} />;
})}
</Steps>
<div className={styles['steps-content']}>{steps && steps[currentStep] ? steps[currentStep].content : null}</div>
<div className="steps-action">
{currentStep > 0 && (
<Button style={{ marginLeft: 8 }} onClick={() => this.prev()}>Previous</Button>
)}
{currentStep < steps.length - 1 && (
<Button type="primary" htmlType="submit">Next</Button>
)}
{currentStep === steps.length - 1 && (
<Button type="primary" htmlType="submit">Done</Button>
)}
</div>
</Fragment>
</Form>
// class variable to user object coming from parent.
user = this.props.user ? this.props.user : undefined;
// Child component
interface IAddressFormProps {
form: WrappedFormUtils;
addresses: IAddress[];
fieldName: string;
}
class Address extends Component<IAddressFormProps> {
totalAddresses: any = this.props.addresses && this.props.addresses ?
this.props.addresses : ([{}] as IAddress[]);
state = {
totalAddresses: this.totalAddresses ? this.totalAddresses : ([] as IAddress[]),
};
addAddress = () => {
this.setState((prevState: any) => ({
totalAddresses: [...prevState.totalAddresses, { lineOne: '', lineTwo: '', lineThree: '', city: '', state: '', zip: '', type: '' }],
}));
};
removeAddress = (index: number) => {
this.setState({
totalAddresses: this.state.totalAddresses.filter((item: IAddress, addressIndex: number) => index !== addressIndex),
});
};
render() {
const { getFieldDecorator } = this.props.form;
const fieldName = this.props.fieldName;
return (
<Fragment>
{this.state.totalAddresses.map((item: IAddress, index: number) => {
return (
<Row key={'container-' + index} type="flex" justify="start" gutter={16}>
<Col span={6}>
<Form.Item label="Type" key={'type-' + index}>
{getFieldDecorator(`${fieldName}[${index}].type`, {
initialValue: item.type,
rules: [{ required: true, message: 'Please input address type!' }],
})(
<Select placeholder="Type">
<Option value="Mailing">Mailing</Option>
<Option value="Business">Business</Option>
<Option value="Home">Home</Option>
<Option value="Other">Other</Option>
</Select>
)}
</Form.Item>
<Form.Item label="Line One" key={'lineOne-' + index}>
{getFieldDecorator(`${fieldName}[${index}].lineOne`, {
initialValue: item.lineOne,
rules: [{ required: true, message: 'Please input line one!' }],
})(<Input placeholder="Line One" />)}
</Form.Item>
<Form.Item label="Line Two" key={'lineTwo-' + index}>
{getFieldDecorator(`${fieldName}[${index}].lineTwo`, {
initialValue: item.lineTwo,
rules: [{ required: false, message: 'Please input line two!' }],
})(<Input placeholder="Line Two" />)}
</Form.Item>
<Form.Item label="Line Three" key={'lineThree-' + index}>
{getFieldDecorator(`${fieldName}[${index}].lineThree`, {
initialValue: item.lineThree,
rules: [{ required: false, message: 'Please input line three!' }],
})(<Input placeholder="Line Three" />)}
</Form.Item>
</Col>
<Col span={9}>
<Form.Item label="City" key={'city-' + index}>
{getFieldDecorator(`${fieldName}[${index}].city`, {
initialValue: item.city,
rules: [{ required: true, message: 'Please input city!' }],
})(<Input placeholder="City" />)}
</Form.Item>
<Form.Item label="State" key={'state-' + index}>
{getFieldDecorator(`${fieldName}[${index}].state`, {
initialValue: item.state,
rules: [{ required: true, message: 'Please input state!' }],
})(<Input placeholder="State" />)}
</Form.Item>
<Form.Item label="Zip" key={'zip-' + index}>
{getFieldDecorator(`${fieldName}[${index}].zip`, {
initialValue: item.zip,
rules: [{ required: true, message: 'Please input zip!' }],
})(<Input placeholder="Zip" />)}
</Form.Item>
</Col>
<Col span={4}>
<Button onClick={() => this.removeAddress(index)}>Remove</Button>
</Col>
</Row>
);
})}
<Button onClick={() => this.addAddress()}>Add address</Button>
</Fragment>
);
}
}
I want to maintain the state of user object throughout the steps means going back and forth.
On form submit, updating the user object.
next = (addEditUser: MutationFn<any, any>) => {
const form = this.props.form;
form.validateFields(async (err: any, values: any) => {
if (err) {
return false;
}
values.id = this.userId;
let variables = this.parentId ? { user: values, parentId: this.parentId } : { user: values };
const result = await addEditUser({ variables: variables });
if (result && result.data) {
if (this.state.currentStep === this.state.steps.length - 1) {
this.props.history.push('/user');
} else {
this.user = Object.assign(this.user, values);
const currentStep = this.state.currentStep + 1;
this.setState({ currentStep });
}
}
});
};
The user object is correctly updated but the child components are not. Why?
Thanks in advance.
In order to pass props to the state you have to use the getDerivedStateFromProps method.
In your child class you can add the following - This is an example for your Address Component where you receive the address from the parent Component via props
static getDerivedStateFromProps(props, state) {
const { address } = props;
if(address !== state.address) return { address }
}
Now here what happens is that if the parent Component sends new props the function checks if the prop.address is different from the state.address - this is the child Component state - and if it's different it's sets it to the value received from the props
Add this method in your Address class and it should work
I'm trying to do a "field 2 does not match field 1" thing here (i.e. "passwords do not match).
There isn't much documentation on the available rules for the antd forms. They point at this project here.
Below is my current form:
const ResetPasswordForm = Form.create()(React.createClass({
getInitialState () {
return {
loading: false
};
},
handleSubmit(e) {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (err) {
failure();
}
if (!err) {
let newPassword = values.newPassword;
let repeatNewPassword = values.repeatNewPassword;
handleResetPassword(newPassword, repeatNewPassword, this.props.token);
}
});
},
render() {
const { getFieldDecorator } = this.props.form;
const newPasswordRules = [
{ required: true, message: 'Please input a new password!' }
];
const repeatNewPassword = [
{ required: true, message: 'Please repeat the new password!' }
];
return (
<Form onSubmit={this.handleSubmit} className="login-form">
<FormItem>
{getFieldDecorator('newPassword', { rules: newPasswordRules })(
<Input addonBefore={<Icon type="lock" />} type="password" placeholder="New password" />
)}
</FormItem>
<FormItem>
{getFieldDecorator('repeatNewPassword', { rules: repeatNewPassword })(
<Input addonBefore={<Icon type="lock" />} type="password" placeholder="Repeat new password" />
)}
</FormItem>
<FormItem>
<Button loading={this.state.loading} type="primary" htmlType="submit" className={css(styles.loginButton)}>
Reset Password
</Button>
</FormItem>
</Form>
);
}
}));
If anyone can point me in the right direction for creating a rule that checks that the first field value matches the second, that'd be awesome!
You can find it in this register form demo: https://ant.design/components/form/#components-form-demo-register
Please, follow these lines:
<Form.Item
name="password"
label="Password"
rules={[
{
required: true,
message: 'Please input your password!',
},
]}
hasFeedback
>
<Input.Password />
</Form.Item>
<Form.Item
name="confirm"
label="Confirm Password"
dependencies={['password']}
hasFeedback
rules={[
{
required: true,
message: 'Please confirm your password!',
},
({ getFieldValue }) => ({
validator(rule, value) {
if (!value || getFieldValue('password') === value) {
return Promise.resolve();
}
return Promise.reject('The two passwords that you entered do not match!');
},
}),
]}
>
<Input.Password />
</Form.Item>