I'm having an input validation problem thats allowing the form to submit without having any selectorValues added. The check I have seems to only check for input inside the textarea but doesn't account for the Add button being pressed.
Here's a sandbox reproducing the issue.
I'm using Semantic-ui-react so my <Form.Field /> looks like this:
<Form.Field required>
<label>Selector Values:</label>
<TextArea
type="text"
placeholder="Enter selector values 1-by-1 or as a comma seperated list."
value={this.state.selectorValue}
onChange={this.handleSelectorValueChange}
required={!this.state.selectorValues.length}
/>
<Button positive fluid onClick={this.addSelectorValue}>
Add
</Button>
<ul>
{this.state.selectorValues.map((value, index) => {
return (
<Card>
<Card.Content>
{value}
<Button
size="mini"
compact
floated="right"
basic
color="red"
onClick={this.removeSelectorValue.bind(this, index)}
>
X
</Button>
</Card.Content>
</Card>
);
})}
</ul>
</Form.Field>
So in the above, <TextArea> has a required prop: !this.state.selectorValues.length. This is only checking for input inside the textarea, it should check that the value has been added by pressing the Add button before allowing the form to submit.
In your addSelectorValue add a check to see if this.state.selectorValue it not empty, if it is just return, this will prevent adding empty values to selectorValues
addSelectorValue = e => {
e.stopPropagation();
e.preventDefault();
if (!this.state.selectorValue) return;
//continue if this.state.selectorValue has a value
};
Before submitting add a check to see if this.selectorValues is empty, if so focus on textarea.
To focus we need to first create a ref to our textarea.
Create a ref to be
attached to a dom element
textareaRef = React.createRef();
// will use our ref to focus the element
focusTextarea = () => {
this.textareaRef.current.focus();
}
handleSubmit = () => {
const { selectorValues } = this.state;
if (!selectorValues.length) {
// call our focusTextarea function when selectorValues is empty
this.focusTextarea();
return;
}
this.setState({ submittedSelectorValues: selectorValues });
};
// attach our ref to Textarea
<Textarea ref={this.textareaRef} />
After some search ... required prop is for decorational purposes only - adding astrisk to field label.
It has nothing to form validation. You need a separate solution for that - try formik or set some condition within submit handler.
Formik plays nicely with yup validation schema - suitable for more complex, dynamic requirements.
Related
I have a form in my react application which is inside a modal pop up. so when the user closes the pop up i want to check if there is any changes made in the form fields.If so i will show a confirmation modal and if no changes i will close the pop up.
<FormProvider {...methods}>
<form onSubmit={handleSubmit(submitDetails)}>
<FormInput
onChange={handleChange(onChange, value)}
onBlur={onBlur}
value={value ?? ""}
name="name"
/>
<FormInput
onChange={handleChange(onChange, value)}
onBlur={onBlur}
value={value ?? ""}
name="age"
/>
<section className="submit-section">
<Button onClick={() => checkUnsavedAndClose()}>Cancel</Button>
<Button type="submit" disabled={!isValid}>
Save
</Button>
</section>
</form>
</FormProvider>
const checkUnsavedAndClose = () => {
// if the form is not updated
closePopUp();
// if the form is updated
alertPopup();
}
What is the best approach to validate the entire form irrespective of number of fields in it. i was thinking of checking whether the form is touched or dirty. but i am not sure that it would be the best solution. any inputs will be highly appreciated
Assuming the "handleChange" function is used for all inputs one thing you could do is use it to hold on state a variable for changes
handleChange(onChange, value) {
value !== originalValue && setState({anyChanges: true}) //or useState and set the state inside the modal
onChange(value)
}
you could also hold which fields were modified and not save to improve the pop-up message
by creating a boolean changed state. that you can change true/false. And you check the value when you try to close the popup.
const [changed, setChanged] = useState(false);
const handleChange() {
// your code
setChanged(true)
}
const checkUnsavedAndClose = () => {
if(changed) {
alertPopup();
} else {
closePopUp();
setChanged(false)
}
I have a component dataModal that has two text boxes, a searchable data table, and a submit button. The Name and Date text boxes need to be filled out manually, and the dataTable is a component that can be searched via it's own text boxes to narrow down what data needs to be entered.
The problem that I am facing is when searching the data table using the text input fields pressing enter causes the form to submit. This is likely happening because the data table is in the form that the submit button is in.
Is there a way to have the text fields in the dataTable not submit the form while retaining the ability to submit the form when pressing enter in the name or date form?
const dataModal = (props) => {
const { data, save, close } = props;
const [name, setName] = useState();
const [date, setDate] = useState();
const handleSubmit = (e) => {
if (e) e.preventDefault();
save(name, date); // DB Call from parent
close(); // Closes the modal
}
return (
<form onSubmit={handleSubmit}>
<customInput
name="NameTextField"
captureInput={(value) => {setName(value)}}
/> // Custom text box component
<customInput
name="DateTextField"
captureInput={(value) => {setDate(value)}}
/> // Custom text box component
<searchableDataTable
name="SearchableDataTable"
data=data
/> // Data Table with customInputs build the same as above that are used for filtering the data table
<Button type="submit" /> // Submission button
</form>
)
}
Moving the data table below the form tabs works, but it causes "ugly" rendering in which the submit button is in the middle of the modal, and not at the bottom. This is the fallback solution if I'm unable to figure this out.
let's assume you pick "Name" to be required (also assuming everything works in your custominput), you could try this:
const [buttonState, setButtonState] = useState();
const isValid = (value) => {
// return true or false
}
...
<customInput
name="NameTextField"
captureInput={(value) => {
setName(value)
setButtonState(isValid(value))
}}
/>
...
<Button {buttonState ? disabled="disabled" : ""} />
Please note isherwood's comment also.
I have inputs that I check for empty, null etc. I do this by storing a state boolean and updating it with onChange events for each input. Each input is stored in a session variable using React.useState. The validation work does work, but if I type something in then hit backspace till the input box is empty you can click submit button and send an empty piece of data and I do not know how to prevent this because I'm checking every onChange event for every input box.
Here is one of my inputs
<input id="editCourse_placeHolder_courseName" type="text" placeholder={edit_Course?.name} onChange={new_category_name}/>
Here is where I validate form input for my state variable
const formValidation = ():void => {
if(state_category_name && state_category_name.trim()){
state_form_Validation = false;
} else {
state_form_Validation = true;
}
setState_form_validation(state_form_Validation);
};
here is my state variable that holds the value of the input box which then gets sent to server.
let [state_category_name, setState_New_Category_Name] = React.useState<string | undefined>("");
State boolean to enable this button if all input is good
let [state_form_Validation, setState_form_validation] = React.useState<boolean>(true);
<input name="view" disabled={state_form_Validation} type="submit" onClick={() => {submitEdit();history.push("/");}} value="Edit"/>
And my on change event
const new_category_name = (e:any):void => {
setState_New_Category_Name(e.target.value);
formValidation();
};
Any help would be great, thanks!
There's a few things wrong here.
First, don't set state variables explicitly, e.g., myVariable = 'new value'. Only update them with the setter, e.g., setMyVariable('new value'). The setter you get back from the useState hook is what ties you into the component lifecycle and will re-render it based on changes.
onChange should be a function not a value, e.g.,
const handleChange = e => /* set state here */
<input id="editCourse_placeHolder_courseName" type="text"
placeholder={edit_Course?.name} onChange={handleChange}
/>
When you implement the onChange handler, each keystroke will update state. Then, I would validate in the body of the component, e.g.,
const isValid = validateForm()
You don't need to maintain the valid status in state. You care if it's valid each render so you can allow a form submit, so just do it in the body of the component. Set the result of the validation to the disabled prop on your button.
<input name="view" disabled={!isValid} type="submit"
onClick={() => {submitEdit();history.push("/");}} value="Edit"
/>
For normal html elements you can get the value of that element through a form.
function handleSubmit(event) {
event.preventDefault()
const formData = new FormData(e.target)
console.log(formData.get("test"))
// logs the value of the html element with the name "test"
}
<form onSubmit={handleSubmit}>
<input name="test" />
<button type="submit">Submit</button>
</form>
The form will assign a key "test" the value of whatever is inside the input on the form's submission.
My question: Is there a way to get this functionality to work with a Semantic UI Dropdown?
Below is ideally what I would think would work but I know doesn't because the component is a wrapper for other elements.
function handleSubmit(event) {
event.preventDefault()
const formData = new FormData(e.target)
console.log(formData.get("test"))
// logs the value of the html element with the name "test"
}
<form onSubmit={handleSubmit}>
<Dropdown name="test" selection options={
[{key: '1', text: 'text', value: '1'}]
}/>
<button type="submit">Submit</button>
</form>
On form submit, whatever value is selected in that dropdown is put into a key called "test" in formData. If no value is selected, an undefined value is given to the key "test."
Is this possible? I have another work around using a basic html selector but would like to not change a lot of already written code.
Yes, but you will need to deconstruct the dropdown component.
const [open, setOpen] = useState(false)
<Dropdown
trigger={<Button onClick={() => setOpen(true)}>Open</Button>}
onClose={() => setOpen(false)}
icon={null}
open={open}
simple
>
<Dropdown.Menu>
... add the options with the "name" tag ...
</Dropdown.Menu>
</Dropdown>
I am using Redux forms with multiple submit buttons along with redux-form <Field />. I need to control the validation of these fields based on click of these buttons. For ex. I need to set a flag to True/False based on click of these buttons so that I can conditionally validate my fields like below:
<Field
name="date"
component={DateFormField}
validate={isSaveDraft && [validateRequiredText]}
floatingLabelText="Date"
fullWidth
helpText="blablabla"
/>
<Field
name="title"
component={TextFormField}
normalizeOnBlur={normalizeTextOnBlur}
validate={!isSaveDraft && [validateRequiredText]}
floatingLabelText="Project title"
fullWidth
helpText="blablabla"
/>
As you can see from above code, I'm conditionally validating my fields with validate={isSaveDraft && [validateRequiredText]} and validate={!isSaveDraft && [validateRequiredText]}
Here are my two submit buttons:
<RaisedButton
label={submitting ? 'Saving Draft...' : 'Save Draft'}
type="button"
onClick={handleSubmit(values => onSubmit())}
disabled={submitting}
primary
/>
<RaisedButton
label={submitting ? 'Submitting Brief...' : 'Submit Brief'}
type="button"
onClick={handleSubmit(values => onSubmit())}
disabled={submitting}
primary
/>
However, I'm not able to achieve it. Please help.
After a lot of headaches and head scratching, I've finally found the solution without making things look ugly. Thanks to a fellow developer for this solution. Original idea was to set a flag on click of the button and conditionally validate the fields. (PS: I'm using Field Level Validations). However, the issue was that the validations are being executed BEFORE the flag setting as the onClick handler wouldn't fire before all the validations are fixed and that logic is buried deep inside redux-forms (perks of unnecessarily overcomplicating simple things by using a library).
Here is the solution:
submit handler
handleSubmit() {
const { bookingCreate, modalShow, navigateTo } = this.props;
const { isDraftAction } = this.state; // my flag
// create record!
return bookingCreate(isDraftAction)
.then(responsePayload => {
...
})
.catch(handleSubmissionError);
}
isDraftAction is the flag which is set (in local state) when the action is called on onClick of both buttons.
My conditional Field level validations
<Field
name="date"
component={DateFormField}
validate={isDraftAction && [validateRequiredText]}
fullWidth
helpText="blablabla"
/>
<Field
name="title"
component={TextFormField}
normalizeOnBlur={normalizeTextOnBlur}
validate={!isDraftAction && [validateRequiredText]}
fullWidth
helpText="blablabla"
/>
My 2 buttons for SAVE RECORD and SUBMIT RECORD.
const submit = handleSubmit(values => onSubmit()); // this is redux-form's submit handler which will in-turn call my own submit handler defined above. (That's where the library hides all the logic and makes developer helpless)
<RaisedButton
label={submitting && isDraft ? 'Saving Draft...' : 'Save Draft'}
type="button"
onClick={() => {
this.props.dispatchAction({ draftAction: true }).then(() => {
submit();
});
}}
disabled={submitting}
primary
/>
<RaisedButton
label={submitting && !isDraft ? 'Submitting Brief...' : 'Submit Brief'}
type="button"
onClick={() => {
this.props.dispatchAction({ draftAction: false }).then(() => {
submit();
});
}}
disabled={submitting}
primary
/>
dispatchAction() is my action function which will FIRST set the flag to true/false THEN call redux-forms inbuilt submit handler. Also, I've extracted the redux-form's submit handler as it is above only for more clarity.
dispatchAction()
dispatchAction={({ draftAction }) =>
new Promise(resolve => {
this.setState({ isDraftAction: draftAction }, resolve);
})
}
use a state variable as isSaveDraft and set it default to false
Add onClick action of button to set state.isSaveDraft = true
validate={(isSaveDraft && [validateRequiredText]} will not work since validate expects a function all the time. Also, field level validation happens onTouch, or onBlur etc. which is way too early as you won't know which button will be clicked.
What you need to do is use a extra flag prop to hold which button was clicked and then use that in the form level validation.
So your Fields will look like this (no more Field level validation):
<Field
name="date"
component={DateFormField}
floatingLabelText="Date"
fullWidth
helpText="blablabla"
/>
...
<RaisedButton
label={submitting ? 'Saving Draft...' : 'Save Draft'}
onMouseDown={() => this.setDraftFlag(true)}
...
/>
<RaisedButton
label={submitting ? 'Submitting Brief...' : 'Submit Brief'}
onMouseDown={() => this.setDraftFlag(false)}
...
/>
I'm using onMouseDown to capture the flag since you're already using onClick for submitting the form. Besides, we need to do the validation before submit anyway.
And then in your form's validate (based on SyncValidation example)
const validate = values => {
const errors = {}
const {isDraft, ...rest} = values
if(isDraft) {
// draft validations
} else {
// other validations
}
return errors
}
export default reduxForm({
form: 'myForm', // a unique identifier for this form
validate, // <--- validation function given to redux-form
})(MyForm)
setDraftFlag can be an action or you can make it part of your Form component like so (I find this easier since you already get change bound to your form):
setDraftFlag(value) {
this.props.change('isDraft', value));
}