I have a group of form inputs that are produced like so:
Please see UPDATE 2 below for full component.
So, if there are three columns then three of this group will be shown in the form. I am trying to extract the data from these inputs but I only need the ids. How can I extract the column id from the TextField?
Also, I need to get the data (i.e. the ids) per group so that they appear in an array:
transformations: [{columnId: 1, ruleId: 4}, {columnId: 2, ruleId: 2}, {columnId:3 , ruleId: 1}]
These are just example values, the main problem as I mentioned, is that I'm not sure how to extract the value of the columnId from the first input. I'm also struggling with getting the multiple sets of data.
Any help with this problem would be much appreciated.
Thanks for your time.
UPDATE:
handleRuleChange looks like this:
handleRuleChange = (e, index, value) => {
this.setState({
ruleId: value
})
}
UPDATE 2:
Here is the component:
import React from 'react'
import Relay from 'react-relay'
import { browserHistory } from 'react-router'
import SelectField from 'material-ui/SelectField'
import MenuItem from 'material-ui/MenuItem'
import TextField from 'material-ui/TextField'
import CreateTransformationSetMutation from '../mutations/CreateTransformationSetMutation'
class CreateTransformationSetDialog extends React.Component {
componentWillMount() {
this.props.setOnDialog({
onSubmit: this.onSubmit,
title: 'Create and Apply Transformation Set'
})
}
initial_state = {
targetTableName: '',
ruleId: 'UnVsZTo1',
}
state = this.initial_state
onSubmit = () => {
const onSuccess = (response) => {
console.log(response)
browserHistory.push('/table')
}
const onFailure = () => {}
Relay.Store.commitUpdate(
new CreateTransformationSetMutation(
{
viewer: this.props.viewer,
version: this.props.viewer.version,
targetTableName: this.state.targetTableName,
transformations: ///this is where I need to get the values///,
}
),
{ onSuccess: onSuccess }
)
}
handleTextChange = (e) => {
this.setState({
targetTableName: e.target.value
})
}
handleRuleChange = (e, index, value) => {
this.setState({
ruleId: value
})
}
render() {
return (
<div>
<TextField
floatingLabelText="Table Name"
value={this.state.targetTableName}
onChange={this.handleTextChange}
/>
<br />
{
this.props.viewer.version.columns.edges.map((edge) => edge.node).map((column) =>
<div key={column.id}>
<TextField
id={column.id}
floatingLabelText="Column"
value={column.name}
disabled={true}
style={{ margin: 12 }}
/>
<SelectField
floatingLabelText="Select a Rule"
value={this.state.ruleId}
onChange={this.handleRuleChange}
style={{ margin: 12 }}
>
{
this.props.viewer.allRules.edges.map(edge => edge.node).map(rule =>
<MenuItem
key={rule.id}
value={rule.id}
primaryText={rule.name}
/>
)
}
</SelectField>
</div>
)
}
</div>
)
}
}
export default Relay.createContainer(CreateTransformationSetDialog, {
fragments: {
viewer: () => Relay.QL`
fragment on Viewer {
${CreateTransformationSetMutation.getFragment('viewer')}
version(id: $modalEntityId) #include(if: $modalShow) {
${CreateTransformationSetMutation.getFragment('version')}
id
name
columns(first: 100) {
edges {
node {
id
name
}
}
}
}
allRules(first: 100) {
edges {
node {
id
name
}
}
}
}
`
},
initialVariables: {
modalEntityId: '',
modalName: '',
modalShow: true,
},
prepareVariables: ({ modalEntityId, modalName }) => {
return {
modalEntityId,
modalName,
modalShow: modalName === 'createTransformationSet'
}
}
})
It is using Relay but that isn't connected to the question, just need to extract the data from the inputs into the transformations array.
This can meet your requirement. Most of code will be understandable. feel free to ask for queries.
class CreateTransformationSetDialog extends React.Component {
componentWillMount() {
this.props.setOnDialog({
onSubmit: this.onSubmit,
title: 'Create and Apply Transformation Set'
})
}
initial_state = {
targetTableName: '',
transformations: [];
ruleId:'UnVsZTo1' //default values for all rules
}
state = this.initial_state
onSubmit = () => {
const onSuccess = (response) => {
console.log(response)
browserHistory.push('/table')
}
const onFailure = () => {}
Relay.Store.commitUpdate(
new CreateTransformationSetMutation(
{
viewer: this.props.viewer,
version: this.props.viewer.version,
targetTableName: this.state.targetTableName,
transformations: this.state.transformations,
}
),
{ onSuccess: onSuccess }
)
}
handleTextChange = (e) => {
this.setState({
targetTableName: e.target.value
})
}
handleRuleChange = (index, ruleId, columnId) => { //TODO: make use of index if needed
let transformations = this.state.transformations;
const isInStateWithIndex = transformations.findIndex((el) => el.columnId === columnId);
if(isInStateWithIndex > -1){
transformations[isInStateWithIndex].ruleId = ruleId; //changed rule
}else{
transformations.push({columnId: columnId, ruleId: ruleId}); //new column added to state.
}
this.setState({
transformations: transformations
}); //do with es6 spread operators to avoid immutability if any
}
render() {
return (
<div>
<TextField
floatingLabelText="Table Name"
value={this.state.targetTableName}
onChange={this.handleTextChange}
/>
<br />
{
this.props.viewer.version.columns.edges.map((edge) => edge.node).map((column) =>
<div key={column.id}>
<TextField
id={column.id}
floatingLabelText="Column"
value={column.name}
disabled={true}
style={{ margin: 12 }}
/>
<SelectField
floatingLabelText="Select a Rule"
value={this.state.ruleId}
onChange={(e, index, value) => this.handleRuleChange(index, value, column.id )}
style={{ margin: 12 }}
>
{
this.props.viewer.allRules.edges.map(edge => edge.node).map(rule =>
<MenuItem
key={rule.id}
value={rule.id}
primaryText={rule.name}
/>
)
}
</SelectField>
</div>
)
}
</div>
)
}
}
Maintaining the state for transformations in state with dynamically created columns.
Related
I have a custom filter values such as:
filterParams: {
values: ['Admin', 'Proje Yƶneticisi', 'Muhasebe'],
defaultToNothingSelected: true,
suppressSelectAll: true
},
However, I can choose multiple values like this. But I don't want to do that, I want to choose only one value instead of multiple choices.
Is there a way to convert this checkbox filter into a radio filter?
Thanks.
You can make a custom filter and there is a video on it: https://www.youtube.com/watch?v=yO3_nTyDv6o
Create a component like this, i am dynamically looking up the options to be displayed based on the extra column parameters supplied in the column def (e.g. thats where props.meta comes in)
import { Button, Radio, RadioGroup, Stack } from "#chakra-ui/react";
import { IFilterParams } from "ag-grid-community";
import React from "react";
import { IRegistryDataColumn } from "../../../../models/RegistryDataColumn";
interface IProps extends IFilterParams {
meta?: IRegistryDataColumn;
}
interface IOption {
value: string;
label: string;
}
export const FilterRadio = React.forwardRef((props: IProps, ref) => {
const [radioOptions, setRadioOptions] = React.useState<IOption[]>([]);
const [filterState, setFilterState] = React.useState<string>();
const handleClear = () => {
setFilterState(undefined);
};
// expose AG Grid Filter Lifecycle callbacks
React.useImperativeHandle(ref, () => {
return {
isFilterActive() {
return filterState !== undefined;
},
doesFilterPass(params) {
const isPass =
params.data[props.colDef.field as string] === filterState;
return isPass;
},
getModel() {},
setModel() {},
};
});
React.useEffect(() => {
props.filterChangedCallback();
}, [filterState]);
React.useEffect(() => {
const radioOptionsUpdate: IOption[] = [];
if (props.meta?.radio_options) {
Object.entries(props.meta.radio_options).forEach(([key, value]) => {
radioOptionsUpdate.push({ value: value.value, label: value.label });
});
}
setRadioOptions(radioOptionsUpdate);
}, [props.meta?.radio_options]);
return (
<Stack p={4} spacing={6} style={{ display: "inline-block" }}>
<Button size="sm" onClick={handleClear}>
Clear filter
</Button>
<RadioGroup onChange={setFilterState} value={filterState}>
<Stack spacing={4}>
{radioOptions.map((option) => (
<Radio key={option.value} value={option.value}>
{option.label}
</Radio>
))}
</Stack>
</RadioGroup>
</Stack>
);
});
And then include it in the column definition:
newCol.filter = FilterRadio;
I want to get AutoComplete onChange data. I got data from onChange((e,value) => console.log(value).
But how to put data on State .
This AutoComplete component behaviour is (Creatable and selectable) . I got all data on onChange but on in the state, If I set the data on State , selectOnFocus is not working.
How to get rid of that?
Thank you
`
<Autocomplete
multiple
limitTags={5}
onChange={(event, newValue) => {
setToEmails(newValue);
}}
filterOptions={(options, params) => {
const filtered = filter(options, params);
if (params.inputValue !== "") {
filtered.push({
inputValue: params.inputValue,
label: `Add "${params.inputValue}"`,
value: nanoid(7),
});
}
return filtered;
}}
selectOnFocus
blurOnSelect
clearOnBlur
handleHomeEndKeys
id="free-solo-with-text-demo"
options={toBlockers}
getOptionLabel={(option) => {
if (typeof option === "string") {
return option;
}
if (option.inputValue) {
return option.inputValue;
}
return option.label;
}}
renderOption={(option) => option.label}
freeSolo
renderInput={(params) => (
<TextField {...params} label="Blockers" variant="outlined" />
)}
/>
`
If I use this , Selectable is not working.
Later on I got solved by myself . This took about 3 days ,
here is full example of Creatable with selectable with formData thing.
const [toEmails, setToEmails] = useState([]);
//if you want to post this data on formData , you have structured this way
const appendedObject = [
{ label: "_creative", value: "node_process_Lh1L8RsafJbUas" },
{ label: "Azim__bro", value: "node_process_LhL1s8RaafJbUas" },
];
const finalBlockers = [...appendedObject, ...toEmails];
formData.append("attachmentList", []);
console.log({ toEmails, finalBlockers });
if (finalBlockers) {
for (const blocker of finalBlockers) {
console.log({ blocker });
formData.append("blockers", JSON.stringify(blocker));
}
}
<Autocomplete
multiple
limitTags={5}
getOptionSelected={(option, value) => {
const optionTitle =
typeof option === "string" ? option : option.value;
const valueTitle =
typeof value === "string" ? value : value.value;
return optionTitle === valueTitle;
}}
onChange={(event, newValue) => {
if (Array.isArray(newValue)) {
const updatedArrayValue = newValue.filter((e) =>
typeof e === "string" ? e.trim() : e
);
const newArrayValue = [...updatedArrayValue];
const updatedArray = newArrayValue.map((item) => {
if (typeof item === "string") {
const newItem = {};
newItem.label = item;
newItem.value = nanoid(12);
newItem.type = "created";
return newItem;
}
return item;
});
setToEmails(updatedArray);
}
}}
filterOptions={(options, params) => {
const filtered = filter(options, params);
if (params.inputValue !== "") {
filtered.push({
inputValue: params.inputValue,
title: `Add "${params.inputValue}"`,
value: nanoid(12),
type: "created",
});
}
return filtered;
}}
selectOnFocus
clearOnBlur
handleHomeEndKeys
id="free-solo-with-text-demo"
options={toBlockers}
filterSelectedOptions
getOptionLabel={(option) => {
// Value selected with enter, right from the input
if (typeof option === "string") {
return option;
}
// Add "xxx" option created dynamically
if (option.inputValue) {
return option.inputValue;
}
// Regular option
return option.label;
}}
renderOption={(option) => option.label}
freeSolo
renderInput={(params) => (
<TextField {...params} label="Blockers" variant="outlined" />
)}
/>
so I have this code for posting to my backend API. Normal form perfectly fine; I managed to post to my database. So I add a Cascader from Ant Design CSS Framework, and every time I selected the value, it produced an error
TypeError: Cannot read property 'value' of undefined
Here is the code:
import React from 'react';
import axios from 'axios';
import { Button, Cascader, Form, Input, Modal } from 'antd';
const FormProduct = Form.Item;
const computerType = [
{
value: 'computer',
label: 'Computer',
},
{
value: 'laptop',
label: 'Laptop',
}
]
export default class FormInventory extends React.Component {
state = {
category: '',
productname: '',
};
handleCategoryChange = event => { this.setState({ category: event.target.value }) }
handleProductNameChange = event => { this.setState({ productname: event.target.value }) }
handleSubmit = event => {
event.preventDefault();
axios.post('myapi',
{
category: this.state.category,
productname: this.state.productname,
})
.then(
function success() {
const modal = Modal.success({
title: 'Success',
content: 'Data successfully add',
});
setTimeout(() => modal.destroy(), 2000);
}
)
}
render() {
return (
<Form onSubmit={this.handleSubmit}>
<FormProduct {...formProductLayout} label="Computer Category">
<Cascader options={computerType} category={this.state.value} onChange={this.handleCategoryChange} />
</FormProduct>
<FormProduct {...formProductLayout} label="Product Name">
<Input type="text" productname={this.state.productname} onChange={this.handleProductNameChange} />
</FormProduct>
<FormProduct wrapperCol={{ span: 12, offset: 2 }}>
<Button type="primary" htmlType="submit">
Add Item
</Button>
</FormProduct>
</Form>
)
}
}
You need to either bind your event handlers in the constructor or use arrow function.
Option 1: Bind
constructor(props) {
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
Option 2: Arrow function
<Input onChange={(e) => this.handleChange(e)} />
According to antd docs you don't need event.target.
https://ant.design/components/cascader/
handleCategoryChange = category => { this.setState({ category }) }
The code above will work fine.
I have a form component:
import React from 'react'
import Relay from 'react-relay'
import { browserHistory } from 'react-router'
import MenuItem from 'material-ui/MenuItem'
import TextField from 'material-ui/TextField'
import CreateTransformationSetMutation from '../mutations/CreateTransformationSetMutation'
class CreateTransformationSetDialog extends React.Component {
componentWillMount() {
this.props.setOnDialog({
onSubmit: this.onSubmit,
title: 'Create and Apply Transformation Set'
})
}
initial_state = {
targetTableName: '',
transformations: this.props.viewer.version.columns.edges.map(edge => edge.node).map(column => {
return {
targetColumnName: column.name,
ruleExpression: '{{' + column.name + '}}'
}
})
}
state = this.initial_state
onSubmit = () => {
const onSuccess = (response) => {
browserHistory.push('/table')
}
const onFailure = () => {}
Relay.Store.commitUpdate(
new CreateTransformationSetMutation(
{
viewer: this.props.viewer,
version: this.props.viewer.version,
targetTableName: this.state.targetTableName,
transformations: JSON.stringify({transformations: this.state.transformations}),
}
),
{ onSuccess: onSuccess }
)
}
handleTableNameChange = (e) => {
this.setState({
targetTableName: e.target.value
})
}
handleTransChange = (e) => {
////what should go here////
}
render() {
return (
<div>
<TextField
floatingLabelText="Table Name"
value={this.state.targetTableName}
onChange={this.handleTableNameChange}
/>
<br />
{
this.props.viewer.version.columns.edges.map((edge) => edge.node).map((column, i) =>
<div key={column.id}>
<TextField
name="targetColumnName"
floatingLabelText="Target Column"
value={this.state.transformations[i].targetColumnName}
onChange={this.handleTransChange}
style={{ margin: 12 }}
/>
<TextField
name="ruleExpression"
floatingLabelText="Rule to Apply"
value={this.state.transformations[i].ruleExpression}
onChange={this.handleTransChange}
style={{ margin: 12 }}
/>
</div>
)
}
</div>
)
}
}
export default Relay.createContainer(CreateTransformationSetDialog, {
fragments: {
viewer: () => Relay.QL`
fragment on Viewer {
${CreateTransformationSetMutation.getFragment('viewer')}
version(id: $modalEntityId) #include(if: $modalShow) {
${CreateTransformationSetMutation.getFragment('version')}
id
name
columns(first: 100) {
edges {
node {
id
name
}
}
}
}
}
`
},
initialVariables: {
modalEntityId: '',
modalName: '',
modalShow: true,
},
prepareVariables: ({ modalEntityId, modalName }) => {
return {
modalEntityId,
modalName,
modalShow: modalName === 'createTransformationSet'
}
}
})
Each input is a set of two material-ui TextFields that have a default value obtained from this.state.transformations. However, I am trying to update the values in these fields and I am not having any luck.
The default state of transformations will look something like this:
transformations: [
{targetColumnName: 'ID', ruleExpression: '{{ID}}'},
{targetColumnName: 'First Name', ruleExpression: '{{FirstName}}'},
{targetColumnName: 'Last Name', ruleExpression: '{{LastName}}'}
]
And I want to be able to update it so that the state can change, for example to:
transformations: [
{targetColumnName: 'ID', ruleExpression: '{{ID}}'},
{targetColumnName: 'First Name', ruleExpression: '{{FirstName}}'},
{targetColumnName: 'Percentage', ruleExpression: '{{Percentage/100}}'}
]
The form is built using Relay but that is not relevant to this question.
Any help with this would be much appreciated.
Thanks for your time.
I had a similar problem and instead I put the value in value attribute, uses defaultValue='some-value' so that it can be mutated and changed.
It seems that what you are trying to do is not possible to protect from XSS (Cross-site scripting)
Write like this:
<TextField
name="targetColumnName"
floatingLabelText="Target Column"
defaultValue={this.state.transformations[i].targetColumnName} {/* change from value to defaultValue */}
onChange={this.handleTransChange}
style={{ margin: 12 }}
/>
I have problems with clearing values from input and select form elements in react form after successful submit through axios library. Just want to mention that i do not use redux-form.
I don't know if I am on the right track here, this is my workflow by far: I wrote a form with react-bootstrap, give every input and select value through props and I access and update the state through these props. I have wrote actions and reducers for updating input values, and one action is dispatched in my component, but the second action and the reducer that is supposed to clear values after submit doesn't work as expected. This is the main problem, I'm not sure if I dispatch FORM_RESET action form in the right place, because I call it in the action that is responsible for posting data to server, and on success callback I dispatch FORM_RESET.
Below is the code relevant for this problem.
/* actionRegister.js */
let _registerUserFailure = (payload) => {
return {
type: types.SAVE_USER_FAILURE,
payload
};
};
let _registerUserSuccess = (payload) => {
return {
type: types.SAVE_USER_SUCCESS,
payload,
is_Active: 0,
isLoading:true
};
};
let _hideNotification = (payload) => {
return {
type: types.HIDE_NOTIFICATION,
payload: ''
};
};
//asynchronous helpers
export function registerUser({ //use redux-thunk for asynchronous dispatch
timezone,
password,
passwordConfirmation,
email,
name
}) {
return dispatch => {
axios.all([axios.post('/auth/signup', {
timezone,
password,
passwordConfirmation,
email,
name,
is_Active: 0
})
// axios.post('/send', {email})
])
.then(axios.spread(res => {
dispatch(_registerUserSuccess(res.data.message));
dispatch(formReset()); //here I dispatch clearing form data
setTimeout(() => {
dispatch(_hideNotification(res.data.message));
}, 10000);
}))
.catch(res => {
dispatch(_registerUserFailure(res.data.message)); //BE validation and passport error message
setTimeout(() => {
dispatch(_hideNotification(res.data.message));
}, 10000);
});
};
}
/* actionForm.js */
//synchronous action creators
export function formUpdate(name, value) {
return {
type: types.FORM_UPDATE_VALUE,
name, //shorthand from name:name introduced in ES2016
value
};
}
export function formReset() {
return {
type: types.FORM_RESET
};
}
/* reducerRegister.js */
const INITIAL_STATE = {
error:{},
is_Active:false,
isLoading:false
};
const reducerSignup = (state = INITIAL_STATE , action) => {
switch(action.type) {
case types.SAVE_USER_SUCCESS:
return { ...state, is_Active:false, isLoading: true, error: { register: action.payload }};
case types.SAVE_USER_FAILURE:
return { ...state, error: { register: action.payload }};
case types.HIDE_NOTIFICATION:
return { ...state , error:{} };
}
return state;
};
export default reducerSignup;
/* reducerForm.js */
const INITIAL_STATE = {
values: {}
};
const reducerUpdate = (state = INITIAL_STATE, action) => {
switch (action.type) {
case types.FORM_UPDATE_VALUE:
return Object.assign({}, state, {
values: Object.assign({}, state.values, {
[action.name]: action.value,
})
});
case types.FORM_RESET:
return INITIAL_STATE;
//here I need isLoading value from reducerRegister.js
}
return state;
};
export default reducerUpdate;
/* SignupForm.js */
import React, {Component} from 'react';
import {reduxForm} from 'redux-form';
import {connect} from 'react-redux';
import map from 'lodash/map';
import timezones from '../../data/timezones';
import styles from '../formElements/formElements.scss';
import {registerUser} from '../../actions/actionRegister';
import {formUpdate} from '../../actions/actionForm';
import FieldGroup from '../formElements/FieldGroup';
import { Form, FormControl, Col, Checkbox, Button, FormGroup } from 'react-bootstrap';
// {... props} passing large number of props wrap in object with spread notation
class SignupForm extends Component { //if component have state it needs to be class
constructor(props) {
super(props);
this.state = {
errors: { //this errors are irrelevant for now
name:'',
email: '',
password: '',
passwordConfirmation:'',
timezone:''
},
};
}
onChange = (event, index, value) => {
this.props.onChange(event.target.name, event.target.value);
};
onSave = (event) => {
event.preventDefault();
this.props.onSave(this.props.values);
}
render() {
let isLoading = this.props.isLoading;
return (
// this.props.handleSubmit is created by reduxForm()
// if the form is valid, it will call this.props.onSubmit
<Form onSubmit={this.onSave} horizontal>
<FieldGroup
id="formControlsName"
type="text"
label="Name"
name="name"
placeholder="Enter Name"
value={this.props.values[name]}
onChange={this.onChange}
help={this.state.errors.name}
/>
<FieldGroup
id="formControlsEmail"
type="text"
label="Email"
name="email"
placeholder="Enter Email"
value={this.props.values[name]}
onChange={this.onChange}
help={this.state.errors.email}
/>
<FieldGroup
id="formControlsPassword"
type="password"
label="Password"
name="password"
placeholder="Enter Password"
value={this.props.values[name]}
onChange={this.onChange}
help={this.state.errors.password}
/>
<FieldGroup
id="formControlsPasswordConfirmation"
type="password"
label="Password Confirmation"
name="passwordConfirmation"
placeholder="Enter Password"
value={this.props.values[name]}
onChange={this.onChange}
help={this.state.errors.passwordConfirmation}
/>
<FieldGroup
id="formControlsTimezone"
label="Time Zone"
name="timezone"
placeholder="Select Time Zone"
componentClass="select"
defaultValue="Select Your Timezone"
value={this.props.values[name]}
onChange={this.onChange}
help={this.state.errors.timezone}
>
<option value="Select Your Timezone">Select Your Timezone</option>
{
map(timezones, (key, value) =>
<option key={key} value={key}>{value}</option>)
}
</FieldGroup>
<FormGroup>
<Col smOffset={4} sm={8}>
<Checkbox>Remember me</Checkbox>
</Col>
</FormGroup>
<FormGroup>
<Col smOffset={4} sm={8}>
<Button type="submit" disabled={isLoading}
onClick={!isLoading ? isLoading : null}
>
{ isLoading ? 'Creating...' : 'Create New Account'}
</Button>
</Col>
</FormGroup>
{this.props.errorMessage && this.props.errorMessage.register &&
<div className="error-container">{this.props.errorMessage.register}</div>}
</Form>
//this.setState({ disabled: true });
//this.props.errorMessage.register == this.props = {errorMessage :{ register: ''}}
);
}
}
function mapStateToProps(state) {
return {
errorMessage: state.signup.error,
isLoading: state.signup.isLoading,
values: state.form.values
};
}
function mapDispatchToProps(dispatch) {
return {
onSave: (values) => dispatch(registerUser(values)),
onChange: (name, value) => dispatch(formUpdate(name, value))
};
}
export default connect(mapStateToProps, mapDispatchToProps)(SignupForm)
;
There is no need to use redux-form :-) You're on the right path and you're calling FORM_RESET action in the right place.
Couple of things:
are you sure you are importing formReset in actionRegister.js?
in reducerForm I would suggest to still return new state here:
case types.FORM_RESET:
return { ...INITIAL_STATE }; // or Object.assign({}, INITIAL_STATE)
And btw. why are you setting isLoading: true on success? I would suggest to create 3 actions instead of 2:
SAVE_USER_START (which you dispatch before sending a request),
set isLoading to true,
SAVE_USER_SUCCESS - set isLoading to false
SAVE_USER_FAILURE - set isLoading to false
I would suggest to look into redux-form library. It provides configuration option to clear fields after submit out of the box.