Adding a new field to a Form
This article walks through the changes required to add a new field to an entity edit form.
See Adding a new field to schema, form and display for the first part of this article.
We are using AntD Form components and you should review https://ant.design/components/form/ first.
Each form comprises three key sections
- Initialise - copy the entity data into the form fields
- Edit - define the input fields and validation rules
- Save - copy the results back into the entity and save it.
Entity Edit Forms in Voluntarily are all named EntityDetailForm.js. e.g. OrgDetailForm.js and will be found in the /components/{entity}/ folder.
Currently our forms are standard React Class components. If you are interested in converting these to use hooks - let me know.
Initialisation
This takes place in Form.Create which you'll usually find at the bottom of the file.
export default Form.create({ name: 'organisation_detail_form', onFieldsChange (props, changedFields) { // props.onChange(changedFields); }, mapPropsToFields (props) { const org = props.org if (!org.info) { org.info = {} } return { name: Form.createFormField({ ...org.name, value: org.name }), about: Form.createFormField({ ...org.info.about, value: org.info.about }), followers: Form.createFormField({ ...org.info.followers, value: org.info.followers }), joiners: Form.createFormField({ ...org.info.joiners, value: org.info.joiners }), members: Form.createFormField({ ...org.info.members, value: org.info.members }), outsiders: Form.createFormField({ ...org.info.outsiders, value: org.info.outsiders }), imgUrl: Form.createFormField({ ...org.imgUrl, value: org.imgUrl }), website: Form.createFormField({ ...org.website, value: org.website }), contactEmail: Form.createFormField({ ...org.contactEmail, value: org.contactEmail }), facebook: Form.createFormField({ ...org.facebook, value: org.facebook }), twitter: Form.createFormField({ ...org.twitter, value: org.twitter }), category: Form.createFormField({ ...org.category, value: org.category }) } }, onValuesChange (_, values) { // console.log('onValuesChange', values) } })(OrgDetailForm)
Form Create is a Higher Order Component (HOC) that wraps the Form Class and adds hooks for onFieldsChange, mapPropsToFields and onValuesChange. Usually are only interested in mapPropsToFields. This function creates a binding between the class 'props' value and the form fields.
We usually link the input field value directly to the entity property but you can translate values at this point.
Edit
Each input field is defined in the render function using <Form.Item> which at a minimum ties the input field to the fieldDecorator - which is handling the init from props and validation.
<Form.Item label='Twitter'> {getFieldDecorator('twitter')(<Input addonBefore='@' />)} </Form.Item>
A more complex field may have validation rules
<Form.Item label={orgCategory}> {getFieldDecorator('category', { rules: [ { required: true, message: 'category is required' } ] })( <Checkbox.Group options={categoryOptions} /> )}
For example to enter a website name we may want to do a format check that the field looks like a URL.
{getFieldDecorator('website', { rules: [ { pattern: /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!$&'()*+,;=.]+$/, message: 'Enter valid URL' } ] })( <Input placeholder='Organisation Website' /> )}
Validate and Save
Finally when the form is submitted we need to validate the form and then save the results. In our code we usually save by preparing the updated object and then calling a callback handler that has been passed in the props so that the parent page holding the form can decide how the save is done e.g. by making an API call. This keeps the form separated from the data movement.
Here we copy out values from the form fields and update the result object. We copy one field at a time so that we can check or transform each item and keep any values in the original object that should be preserved - such as _id.
handleSubmit = (e) => { e.preventDefault() this.props.form.validateFields((err, values) => { if (!err) { // preserve the id and other values not edited by form. const org = this.props.org // update the rest from the form values. org.name = values.name org.slug = slug(values.name) org.info.about = values.about org.info.followers = values.followers org.info.joiners = values.joiners org.info.members = values.members org.info.outsiders = values.outsiders org.imgUrl = values.imgUrl org.website = values.website org.twitter = values.twitter org.facebook = values.facebook org.contactEmail = values.contactEmail org.category = values.category window.scrollTo(0, 0) this.props.onSubmit(this.props.org) } }) }
This code could also use the ... spread operator to merge the old and new values.
At the end of the handler we call the props.onSubmit to perform the save along with any feedback and changing the page to view the details.
Related articles