Closed 1django closed 9 years ago
Ok - let me think about the best practice for this.
Thanks.
A workaround is completely acceptable for the moment, as I'm facing a deadline pretty soon!
ok so here is how I would approach this: note: you need to be on latest version of library > 5.0.4
//other imports
import strategy from 'joi-validation-strategy';
const Component = React.createClass({
//..other methods
componentDidMount: function() {
this.isFormValid();
},
componentWillUpdate: function() {
this.isFormValid();
},
isFormValid: function() {
const options = {
abortEarly: false,
allowUnknown: true,
};
// might need to inspect the return type here, this has not been tested
this.setState({
showSubmit: Object.keys(strategy(options).validate(this.getValidatorData(), this.validatorTypes()).length === 0,
});
}
});
export default Component;
So now you have access to the strategy to validate in a stateless way. I think this is the correct pattern. Just add a state variable for showSubmit
and it will be enabled whenever the form is fully valid and only then.
actually this should work pre 5.0 if you just import the strategy. Let me know what version your on.
also edited the above example, the validation call returns an array that needs to me checked for length
Thanks for the quick responses.
I'm on version 4.2.0.
edited example to reflect 4.x
Thanks.
I'm getting the following error: Uncaught TypeError: this.getValidatorData is not a function
.
The error appears to be coming from: showSubmit: strategy(options).validate(this.getValidatorData(), this.validatorTypes()).error === null,
Changing this.validatorTypes()
to this.validatorTypes
removed the previous error, but led me to another: this.getValidatorData is not a function
. Which is true for my component at least, but not sure what this lib exposes.
Here's a slightly condensed version of my component:
var React = require('react/addons');
var ValidationMixin = require('react-validation-mixin');
var Joi = require('joi');
import strategy from 'joi-validation-strategy';
module.exports = React.createClass({
mixins: [ValidationMixin, React.addons.LinkedStateMixin],
validatorTypes: {
firstName: Joi.string().required().regex(nameRegex).max(255).label('First Name'),
},
getInitialState: function() {
return {
showSubmit: false,
firstName: null,
}
},
componentDidMount: function() {
this.isFormValid();
},
componentWillUpdate: function() {
this.isFormValid();
},
isFormValid: function() {
const options = {
abortEarly: false,
allowUnknown: true,
};
this.setState({
showSubmit: strategy(options).validate(this.getValidatorData(), this.validatorTypes).error === null,
});
},
render: function(){
return (
<div>
<div className={this.getClasses('firstName')}>
<label htmlFor="firstName" className={this.getLabelClasses('firstName')}>First Name</label>
<input type="text" className="form-control" id="firstName" ref="firstName" defaultValue={this.props.fieldValues.firstName} placeholder="First Name" valueLink={this.linkState('firstName')} onBlur={this.handleValidation('firstName')} />
{this.renderCustomHelpText('firstName')}
</div>
<div className="col-md-12">
<button disabled={!this.showSubmit} id="continue-btn" className="btn btn-primary" onClick={this.saveAndContinue}>Save and Continue</button>
</div>
</div>
)
},
// more methods
});
if your component did not implement getValididatorData
then replace it with this.state
// might need to inspect the return type here, this has not been tested
this.setState({
showSubmit: Object.keys(strategy(options).validate(this.state, this.validatorTypes).length === 0,
});
also your button is incorrect:
disabled={!this.showSubmit}
should be:
disabled={!this.state.showSubmit}
And if your not using es6 transpiler replace 'const' with 'var' and
import strategy from 'joi-validation-strategy';
with
var strategy = require( 'joi-validation-strategy');
Seems closer, but not quite there.
With the above changes, and the following:
isFormValid: function() {
const options = {
abortEarly: false,
allowUnknown: true,
};
this.setState({
showSubmit: Object.keys(strategy(options).validate(this.state, this.validatorTypes)).length === 0,
});
}
I get a Uncaught RangeError: Maximum call stack size exceeded
error.
The error can be averted by commenting out the call to this.isFormValid()
in componentWillUpdate
, but of course then the showSubmit
state variable is never updated, and the form submit is never possible.
Thank you again for your help. Much appreciated...
Ok try componentWillReceiveProps
instead
This is likely what you were trying to do, just more succinctly and leveraging reacts component lifecycle, but here is what finally worked for me:
var React = require('react/addons');
var ValidationMixin = require('react-validation-mixin');
var Joi = require('joi');
var strategy = require( 'joi-validation-strategy');
module.exports = React.createClass({
mixins: [ValidationMixin, React.addons.LinkedStateMixin],
validatorTypes: {
firstName: Joi.string().required().regex(nameRegex).max(255).label('First Name')
},
getInitialState: function() {
return {
showSubmit: false,
firstName: null,
};
},
componentDidMount: function() {
this.isFormValid();
},
// callback for this.handleValidation onBlur of form inputs
// my hacky substitute for using componentWillUpdate
handleValidationCallback: function(){
this.isFormValid();
},
isFormValid: function() {
const options = {
abortEarly: false,
allowUnknown: true,
};
var valMessages = strategy(options).validate(this.state, this.validatorTypes),
isValid = true;
for (var p in valMessages) {
if(valMessages.hasOwnProperty(p) && valMessages[p].length){
isValid = false;
break;
}
}
this.setState({
showSubmit: isValid
});
},
render: function(){
return (
<div>
<div className="col-md-6">
<fieldset>
<div className={this.getClasses('firstName')}>
<label htmlFor="firstName" className={this.getLabelClasses('firstName')}>First Name</label>
<input type="text" className="form-control" id="firstName" ref="firstName" defaultValue={this.props.fieldValues.firstName} placeholder="First Name" valueLink={this.linkState('firstName')} onBlur={this.handleValidation('firstName', this.handleValidationCallback)} />
{this.renderCustomHelpText('firstName')}
</div>
</fieldset>
</div>
<div className="col-md-12">
<button disabled={!this.state.showSubmit} id="continue-btn" className="btn btn-primary" onClick={this.saveAndContinue}>Save and Continue</button>
</div>
</div>
)
},
// more methods...
});
nice! glad you got it to workout. I'll try to put together an example of this later too.
I'd like to disable my form submit button, and leave it disabled, until the user has (successfully) filled out the form. I'm wondering the preferred method is to achieve this?
My attempts of disabling the form submit button based on
this.isValid()
have failed. I.e., assuming a form with some validated inputs...my button would look something like this:<button disabled={!this.isValid()} onClick={this.saveAndContinue}>Submit</button>
.On initial rendering, this button is not disabled (I'm guessing because validation has not run on the form yet). The button becomes disabled as soon as I interact with a (validated) input.
I tried calling
this.validate()
oncomponentDidMount
, which successfully disables the button, but also displays all of the error messages on my form. This is not desirable in my case.Suggestions on how to achieve disabling the form button until the form is valid, w/o showing all form errors on initial render?
Thanks.