Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async Validator #103

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 63 additions & 12 deletions dist/simple-react-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ function () {
isBlank: function isBlank(value) {
return typeof value === 'undefined' || value === null || value === '';
},
isAsync: function isAsync(rule, rules) {
return rules[rule].hasOwnProperty('asyncRule');
},
normalizeValues: function normalizeValues(value, validation) {
return [this.valueOrEmptyString(value), this.getValidation(validation), this.getOptions(validation)];
},
Expand Down Expand Up @@ -162,6 +165,7 @@ function () {
this.fields = {};
this.visibleFields = [];
this.errorMessages = {};
this.asyncValidators = {};
this.messagesShown = false;
this.rules = _objectSpread({
accepted: {
Expand Down Expand Up @@ -468,6 +472,37 @@ function () {

return true;
}
}, {
key: "asyncValid",
value: function asyncValid(completion) {
this.failedAsyncValidator = null;
if (!this.allValid()) return completion.fail();
if (Object.keys(this.asyncValidators).length === 0) return completion.pass();
this.currentAsyncValidator = Object.keys(this.asyncValidators)[0];
var validator = this.asyncValidators[this.currentAsyncValidator];
validator.rules[validator.rule].asyncRule(validator.value, validator.params, this, completion);
}
}, {
key: "pass",
value: function pass(completion) {
var keys = Object.keys(this.asyncValidators);
var index = keys.indexOf(this.currentAsyncValidator);

if (index >= keys.length - 1) {
return completion.pass();
} else {
this.currentAsyncValidator = keys[index + 1];
var validator = this.asyncValidators[this.currentAsyncValidator];
validator.rules[validator.rule].asyncRule(validator.value, validator.params, this, completion);
}
}
}, {
key: "fail",
value: function fail(completion) {
var validator = this.asyncValidators[this.currentAsyncValidator];
this.failedAsyncValidator = this.currentAsyncValidator;
completion.fail();
}
}, {
key: "fieldValid",
value: function fieldValid(field) {
Expand Down Expand Up @@ -524,19 +559,19 @@ function () {
rule = _this$helpers$normali2[1],
params = _this$helpers$normali2[2];

if (!this.helpers.passes(rule, value, params, rules)) {
this.fields[field] = false;
var message = this.helpers.message(rule, field, options, rules);

if (params.length > 0 && rules[rule].hasOwnProperty('messageReplace')) {
message = rules[rule].messageReplace(message, params);
}

this.errorMessages[field] = message;
if (this.helpers.isAsync(rule, rules)) {
this.asyncValidators["".concat(field, ":").concat(rule)] = {
value: value,
rule: rule,
params: params,
field: field,
options: options,
rules: rules
};
}

if (this.messagesShown || this.visibleFields.includes(field)) {
return this.helpers.element(message, options);
}
if (!this.helpers.passes(rule, value, params, rules) || this.helpers.isAsync(rule, rules) && this.failedAsyncValidator == "".concat(field, ":").concat(rule)) {
return this.fieldFailure(field, rule, rules, options, params);
}
}
} catch (err) {
Expand All @@ -554,6 +589,22 @@ function () {
}
}
}
}, {
key: "fieldFailure",
value: function fieldFailure(field, rule, rules, options, params) {
this.fields[field] = false;
var message = this.helpers.message(rule, field, options, rules);

if (params.length > 0 && rules[rule].hasOwnProperty('messageReplace')) {
message = rules[rule].messageReplace(message, params);
}

this.errorMessages[field] = message;

if (this.messagesShown) {
return this.helpers.element(message, options);
}
}
}]);

return SimpleReactValidator;
Expand Down
2 changes: 1 addition & 1 deletion dist/simple-react-validator.min.js

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions docs/dev-index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<script type="text/javascript" src="../dist/simple-react-validator.js"></script>
<script type="text/javascript" src="../dist/locale/fr.js"></script>
<script src="example.jsx" type="text/babel"></script>
<script src="example-async.jsx" type="text/babel"></script>

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<style>
Expand All @@ -25,5 +26,6 @@
open terminal, run <code>npm run serve</code> in the project directory, and navigate to <a href="http://localhost:3000/docs">localhost:3000/docs/dev-index.html</a>
</div>
</div>
<div id="exampleAsyncForm"></div>
</body>
</html>
68 changes: 68 additions & 0 deletions docs/example-async.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
class ExampleAsyncForm extends React.Component {

constructor(props) {
super(props);
this.state = {
processing: false
};
this.validator = new SimpleReactValidator({
className: 'text-danger',
validators: {
unique: {
message: 'Not a unique email.',
asyncRule: function(val, params, validator, completion) {
setTimeout(() => validator.fail(completion), 1000);
}
}
}
});
}

submitForm() {
this.setState({processing: true});
this.validator.asyncValid({
pass: () => {
alert('You submitted the form and stuff!');
this.setState({processing: false});
},
fail: () => {
console.log(this.validator);
this.validator.showMessages();
this.setState({processing: false});
}
});
}

handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}

render() {
return (
<div className="container card my-4">
<div className="card-body">
<h3>Example Async Form</h3>
<small className="text-muted">Click submit to view messages.</small>
<hr />
<div className="row">
<div className="col-sm-6 col-md-4">
<div className="form-group">
<label>Email</label>
<input className="form-control" type="email" name="email" value={this.state.email} onChange={this.handleInputChange.bind(this)} />
{this.validator.message('email', this.state.email, 'email|unique')}
</div>
</div>
</div>
<button className="btn btn-primary" onClick={this.submitForm.bind(this)} disabled={this.state.processing}>Submit</button>
</div>
</div>
);
}
}

ReactDOM.render(<ExampleAsyncForm />, document.getElementById('exampleAsyncForm'));
71 changes: 59 additions & 12 deletions src/simple-react-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class SimpleReactValidator {
this.fields = {};
this.visibleFields = [];
this.errorMessages = {};
this.asyncValidators = {};
this.messagesShown = false;
this.rules = {
accepted : {message: 'The :attribute must be accepted.', rule: val => val === true, required: true},
Expand Down Expand Up @@ -111,6 +112,34 @@ class SimpleReactValidator {
return true;
}

asyncValid(completion) {
this.failedAsyncValidator = null;
if (!this.allValid()) return completion.fail();
if (Object.keys(this.asyncValidators).length === 0 ) return completion.pass();

this.currentAsyncValidator = Object.keys(this.asyncValidators)[0];
const validator = this.asyncValidators[this.currentAsyncValidator];
validator.rules[validator.rule].asyncRule(validator.value, validator.params, this, completion);
}

pass(completion) {
const keys = Object.keys(this.asyncValidators);
const index = keys.indexOf(this.currentAsyncValidator);
if (index >= keys.length - 1) {
return completion.pass();
} else {
this.currentAsyncValidator = keys[index+1];
const validator = this.asyncValidators[this.currentAsyncValidator];
validator.rules[validator.rule].asyncRule(validator.value, validator.params, this, completion);
}
}

fail(completion) {
const validator = this.asyncValidators[this.currentAsyncValidator];
this.failedAsyncValidator = this.currentAsyncValidator;
completion.fail();
}

fieldValid(field) {
return this.fields.hasOwnProperty(field) && this.fields[field] === true;
}
Expand Down Expand Up @@ -142,22 +171,36 @@ class SimpleReactValidator {
var rules = options.validators ? {...this.rules, ...options.validators} : this.rules;
for (let validation of validations) {
let [value, rule, params] = this.helpers.normalizeValues(inputValue, validation);
if (!this.helpers.passes(rule, value, params, rules)) {
this.fields[field] = false;
let message = this.helpers.message(rule, field, options, rules);

if (params.length > 0 && rules[rule].hasOwnProperty('messageReplace')) {
message = rules[rule].messageReplace(message, params);
}

this.errorMessages[field] = message;
if (this.messagesShown || this.visibleFields.includes(field)) {
return this.helpers.element(message, options);
}
if (this.helpers.isAsync(rule, rules)) {
this.asyncValidators[`${field}:${rule}`] = {
value: value,
rule: rule,
params: params,
field: field,
options: options,
rules: rules
};
}
if (!this.helpers.passes(rule, value, params, rules) || (this.helpers.isAsync(rule, rules) && this.failedAsyncValidator == `${field}:${rule}`)) {
return this.fieldFailure(field, rule, rules, options, params);
}
}
}

fieldFailure(field, rule, rules, options, params) {
this.fields[field] = false;
let message = this.helpers.message(rule, field, options, rules);

if (params.length > 0 && rules[rule].hasOwnProperty('messageReplace')) {
message = rules[rule].messageReplace(message, params);
}

this.errorMessages[field] = message;
if (this.messagesShown) {
return this.helpers.element(message, options);
}
}

helpers = {
parent: this,

Expand All @@ -180,6 +223,10 @@ class SimpleReactValidator {
return typeof(value) === 'undefined' || value === null || value === '';
},

isAsync(rule, rules) {
return rules[rule].hasOwnProperty('asyncRule');
},

normalizeValues(value, validation) {
return [this.valueOrEmptyString(value), this.getValidation(validation), this.getOptions(validation)];
},
Expand Down