formsy-react
formsy-react copied to clipboard
Trigger validations onSubmit vs onBlur option
is this currently possible?
@th3fallen I am currently using formsy to only show the messages after a user leaves the input (instead of on typing). The validations are still done as the user types but I am only showing them after the user modifies the input or clicks the submit button. I control that in my input wrapper component by choosing when the error message gets displayed or not. Something like:
if (!this.state.isFieldEdited && !this.props.isFormSubmitted()) errorMessage = null;
Note that isFieldEdited is part of my component and isFormSubmitted() is from Formsy.
I've been wanting this for a long time too. What @mikelyncheski suggest is certainly possible and a good way to do it, but sometimes you may want to optimize a demanding validation by not doing it on every keyup. Debounce would also be another nice option. We could probably add this as a non-breaking change as a prop which works as before by default, something like:
<SomeFormsyComponent validateOn={'blur'} />
with 'keyup' as default, or whatever is the default atm
Ok, we're all stupid. Sorry for calling you stupid, but I'm calling myself stupid too so it's fine ;)
Upon further investigation turns out this is up to the implementation of a formsy field component. this.props.setValue
provided by withFormsy has a secon parameter, boolean, which tells formsy whether to validate the new value or not. This altered example from readme does exactly what we need:
class MyInput extends React.Component {
constructor(props) {
super(props);
this.changeValue = this.changeValue.bind(this);
this.validateValue = this.validateValue.bind(this);
}
changeValue(event) {
// setValue() will set the value of the component, which in
// turn will validate it and the rest of the form
// Important: Don't skip this step. This pattern is required
// for Formsy to work.
this.props.setValue(event.currentTarget.value, false);
}
validateValue(event) {
this.props.setValue(event.currentTarget.value);
}
render() {
// An error message is returned only if the component is invalid
const errorMessage = this.props.getErrorMessage();
return (
<div>
<input
onChange={this.changeValue}
onBlur={this.validateValue}
type="text"
value={this.props.getValue() || ''}
/>
<span>{errorMessage}</span>
</div>
);
}
}
As a followup on this one. When you use onBlur to set the error, a common pattern is to clear the error when the user starts typing.
- Enter invalid value
- Blur the field
- Error appears
- Change the value in the field
- Error message goes away
- Blur the field
- Validation occurs again
Right now, it seems that Formsy does not internally handle steps 4-5, correct.
Is there any solution for this issue? we actually need to trigger validations onSubmit and onBlur , it's really needed
@hungbang There is currently no solution for this issue, but PRs are welcome as well as failing tests or even just edits to API.md
with the desired feature spec. I think anyone will find the project very easy to work with.
Hi, I wanted to implement this behavior so I went through the source code of formsy and came up with this solution, but I don't know much about the structure and the way formsy validates the forms in it's internals (frankly don't have that much time), but it seems to be working as expected.
That's why i wanted to ask @rkuykendall if this is a possible proof of concept that could be implemented.
ValidationInput.js
import { withFormsy } from "formsy-react";
import React from "react";
class ValidationInput extends React.Component {
constructor(props) {
super(props);
this.changeValue = this.changeValue.bind(this);
this.validateValue = this.validateValue.bind(this);
}
changeValue(event) {
// this is an add method via the InputWrapper
// that resets the validation properties if an error
// is present
if (this.props.showError) {
this.props.resetValidation();
}
// setValue() will set the value of the component, which in
// turn will validate it and the rest of the form
// Important: Don't skip this step. This pattern is required
// for Formsy to work.
if (this.props.isValidValue(event.currentTarget.value)) {
this.props.setValue(event.currentTarget.value);
} else {
this.props.setValue(event.currentTarget.value, false);
}
}
validateValue(event) {
this.props.setValue(event.currentTarget.value);
}
keyDown = event => {
// If we press enter, we want to validate the input (omitting
// the false argument)
if (event.keyCode == "13") {
this.props.setValue(event.currentTarget.value);
}
};
render() {
// An error message is returned only if the component is invalid
const {
errorMessage,
className,
showRequired,
isFormSubmitted,
showError,
type,
value,
placeholder,
children
} = this.props;
let nClassName = className ? className : "form-control";
nClassName =
isFormSubmitted && showRequired
? nClassName + " is-invalid"
: showError
? nClassName + " is-invalid"
: nClassName;
return (
<fieldset className="form-group">
<input
className={nClassName}
type={type ? type : "text"}
value={value}
onChange={this.changeValue}
onBlur={this.validateValue}
placeholder={placeholder}
onKeyDown={this.keyDown}
/>
{children ? children : null}
{errorMessage ? (
<div className="invalid-feedback">{errorMessage}</div>
) : null}
</fieldset>
);
}
}
class WrappedInput extends withFormsy(ValidationInput) {
resetValidation = () => {
const { pristineValue } = this.state;
const { validate } = this.context;
this.setState({
isPristine: true,
isValid: true
});
};
render() {
const { innerRef } = this.props;
const propsForElement = {
...this.props,
errorMessage: this.getErrorMessage(),
errorMessages: this.getErrorMessages(),
hasValue: this.hasValue(),
isFormDisabled: this.isFormDisabled(),
isFormSubmitted: this.isFormSubmitted(),
isPristine: this.isPristine(),
isRequired: this.isRequired(),
isValid: this.isValid(),
isValidValue: this.isValidValue,
resetValue: this.resetValue,
setValidations: this.setValidations,
setValue: this.setValue,
showError: this.showError(),
showRequired: this.showRequired(),
value: this.getValue(),
resetValidation: this.resetValidation
};
if (innerRef) {
propsForElement.ref = innerRef;
}
return React.createElement(ValidationInput, propsForElement);
}
}
export default WrappedInput;
And the form source code:
<Formsy onValidSubmit={this.submitForm}>
<ValidationInput
name="email"
validations="isEmail"
validationError={strings.form.errors.emailFormat}
placeholder={strings.auth.email}
required
/>
<div className="text-center">
<button
className="btn btn-primary pull-center w-50 mt-5"
type="submit"
disabled={this.props.inProgress}
>
{strings.auth.login}
</button>
</div>
</Formsy>
EDIT
Added onKeyDown check for validation of the input on enter
key press