jQuery Validation: abstracting rules - javascript

Im using jQuery's validation which can be found here: http://docs.jquery.com/Plugins/Validation/rules#.22add.22rules
I currently have some of my 'custom' rules set up like so:
else if(valID =="#shipForm")
{
$("#fName").rules("add",{regexName: "^[a-zA-Z]+(([\'\-][a-zA-Z])?[a-zA-Z]*)*$",minlength: 2,messages:{minlength:"Must be 2 characters."}});
$("#lName").rules("add",{regexName: "^[a-zA-Z]+(([\'\-][a-zA-Z])?[a-zA-Z]*)*$",minlength: 2,messages:{minlength:"Must be 2 characters."}});
$("#sAdd1").rules("add",{stringCheck: "",minlength:2,messages:{minlength:"Enter your complete street address."}});
$("#sAdd2").rules("add",{stringCheck: ""});
$("#city").rules("add",{stringCheck: "",minlength:2,messages:{minlength:"Enter the full name of your city"}});
$("#zipcode").rules("add",{stripZip: ""});
$("#phoneIn").rules("add",{stripPhone: "",maxlength:15,messages:{maxlength:"Phone number exceeds allowed length"}});
$("#altPhone").rules("add",{stripPhone: ""});
$("#state").rules("add",{checkMenu: ""});
$("#country").rules("add",{checkMenu: ""});
}
What I was hoping to do.. is abstracting the .rules out and being able to grab them from a function. My issue is that they are not just strings, so im lacking an idea of how i could bring info from another function and populate the .rules("passed value of rule")
This doesnt work but this is an example of something i was kinda hoping for
function getRule(rule)
{
switch (rule)
{
case "fName":
return "\"add\",{regexName: \"^[a-zA-Z]+(([\'\-][a-zA-Z])?[a-zA-Z]*)*$\",minlength: 2,messages:{minlength:\"Must be 2 characters.\"}}";
break;
}
}
But I obviously cant just pass a string back and run it back into the .rules.
Any idea's?

You could return an object
function getRule(rule)
{
switch (rule)
{
case "fName":
return {
param1 : "add",
param2 : {
regexName: "^[a-zA-Z]+(([\'\-][a-zA-Z])?[a-zA-Z]*)*$",
minlength: 2,
messages:{
minlength:"Must be 2 characters."
}
}
};
}
}
now if you call getRule it will return an object like this
var myruleDef = getRule(rule);
$(selector).rules(myruleDef.param1, myruleDef.param2);

You can store your rules in a map:
var rules = {
fName: {
regexName: "^[a-zA-Z]+(([\'\-][a-zA-Z])?[a-zA-Z]*)*$",
minlength: 2,
messages: {
minlength: "Must be 2 characters."
}
},
lName: { // ... }
// ...
};
And apply all of them with:
for (var id in rules) {
$("#" + id).rules("add", rules[id]);
}
Note: You don't need a break after a return.
Note2: Wrap and indent your object literals for readability.

Related

validate multiple variables without writing multiple if else statements

Lets say we have 3 variables and I want to check them empty or not without using multiple if else blocks.
let firstName = "adem"
let lastName = "corona"
let email = "adamcorons#gmai.com"
If(firstName === " " && lastName !== " " && email !== " "){
Console.log("first name empty")
} else if .......
What is the best way of solving this?
Thanks a lot
You can avoid chained if-else stataments by returning directly from an if statement. For example, you can have a function such as this one:
function isInputValid({ firstName, lastName, email }) {
if (firstName === '') {
return false;
}
if (lastName === '') {
return false;
}
if (email === '') {
return false;
}
return true;
}
console.log(isInputValid({ firstName: 'adem', lastName: 'corona', email: 'adamcorons#gmai.com' }));
console.log(isInputValid({ firstName: 'adem', lastName: '', email: 'adamcorons#gmai.com' }));
Instead of a boolean value, you could also return an object containing an error message, so you can point out which field is missing.
You could try looping over a required array of fields like this to make it short and flexible:
const validate = (values, required) = {
let errors = {}
required.map(field => {
if (values[field] == '') {
errors[field] = 'Required';
}
}
return errors;
}
const required = ['firstName', 'lastName', 'email'];
const values = {
firstName = '',
lastname = 'test',
email = ''
}
const errors = validate(values, required);
console.log(errors);
// errors = { firstName: 'Required', email: 'Required' }
Making the values an object instead of individual parameters makes it possible to access them dynamically in a loop. This might work depending on what your needs and requirements are.
Then to check if errors exist, just see if the size of the object is 0 by converting it to an array:
if (Object.keys(errors).length == 0) {
// No errors, continue as valid
} else {
// There are errors, handle them as needed.
}
Another great way is to use YUP. It is possible to provide validation anywhere. I always use it. Here is an example.
const schema = Yup.object({
last_name: Yup.string()
.when('first_name', {
is: true,
then: Yup.string().required().label("Last Name"),
}),
});
You can use a similar as following. It also has many other amenities. You can find all the information from the official website below.
https://github.com/jquense/yup
I guess you can try something like this
// declare vars where you should, and add the vars you want to test in an object
let result = [];
let toTest= {
firstName = "adem",
lastName = "corona",
email: "adamcorons#gmai.com",
}
// declare this function to test your values
function testValue(){
Object.keys(toTest).map(key => {
if(!toTest[key]){
result.push(key);
}
}
}
// where you need, call your function to test your strings
testValue();
// in your result array you will have the keys of all the empty vars in your object
Note: if its for field validation you have some plugins like Yup(if Formik) or validate.js that are great for it ! have a look ! https://validatejs.org/
EDIT: Changed the response to an array so you can have all the results. I recommande you to set result as an object {key: errorMessage, ...} so its easier for you to use him after (ex: call the error.nameOfInput in your form to display the error.
EDIT2:
With object result would look like this
// declare vars where you should, and add the vars you want to test in an object
let error= {};
let toTest= {
firstName = "adem",
lastName = "corona",
email: "adamcorons#gmai.com",
}
// declare this function to test your values
function testValue(){
Object.keys(toTest).map(key => {
if(!toTest[key]){
error[key]={`${key} cannot be empty`};
}
}
}
// where you need, call your function to test your strings
testValue();
// in your render
<input name="firstName" ...props />
{error.firstName && <div className='error'>{error.firstName} </div> }
I believe you can't achieve this with plain if like in your example. You would need to have a validation schema and function that you run to validate, like most libraries do it. So you would need have some kind of initial object that couples the field names, their rules and error messages. But then you would need to use object with properties instead of plain variables. With those you cannot avoid checking them without separate if statements.
For reference check how joi or yup works. Although I do understand that using one of those might be too much for you, so I'd just check each field separately.

Javascript returning a string as a regex for jquery validate add method

I have this jquery validate add method below with a specific regex.
What I want is to create a method that returns the regex, like in my second example, but the second example doesn't work. I get some error that says 'Object doesn't support property or method 'test'
if (scope.countryCode == "DE") {
$.validator.addMethod('PostalCodeError',
function(value) {
return /^(?!01000|99999)(0[1-9]\d{3}|[1-9]\d{4})$/.test(value);
}, 'Please enter a valid German postal code.');
$("#PostalCode").rules("add", {
PostalCodeError: true
});
}
I want something like this below
$.validator.addMethod('PostalCodeError',
function(value) {
return GetCountryRegex().test(value);
}, 'Please enter a valid postal code.');
$("#PostalCode").rules("add", {
PostalCodeError: true
});
function GetCountryRegex() {
if (scope.countryCode == "DE") {
return '/^(?!01000|99999)(0[1-9]\d{3}|[1-9]\d{4})$/';
}
if (scope.countryCode == "AT") {
return '/^\\d{4}$/';
}
}
So, in GetCountryRegex you're not actually returning RegEx, your returning strings.
Use new RegExp on the returned value to convert the strings to RegExp:
function GetCountryRegex() {
if (scope.countryCode == "DE") {
return '/^(?!01000|99999)(0[1-9]\d{3}|[1-9]\d{4})$/';
}
if (scope.countryCode == "AT") {
return '/^\\d{4}$/';
}
}
var regExp = new RegExp(GetCountryRegex());
regExp.test(...);

Simple grammar checker program - optimal data structure

I want to created a simple game for English that checks someone's sentences. They are able to construct their sentence using a fixed set of words from a word bank.
The word bank might be something scrambled like:
[want, eat, I, hi, to]
Then they'd create their sentence in the correct order:
hi I want to eat.
I asked this question on English SO as it originally pertained to grammatical questions- the question has evolved into more of a data structures question. You can read more about it at that link. My original thought to check sentence grammar using a set of generic English rules seemed like it could quickly grow too complex. It was recommended I just match using hard coded checks, shown below.
Before I further define these checks, I was wondering if a better data structure/method was known to check grammar for this purpose.
if (input === the_answer) {
msg = correct!
} else {
msg = 'Try again: ' + this.grammarRules(input, the_answer));
}
Language_System.prototype.grammarRules = function(input, answer) {
var grammar_hints = {
quest1 : {
task1 : [
'The subject, Bob, needs to be first',
'The phrase is Hello there'
]
}
};
var grammar_rules = {
quest1 : {
task1 : function (input, answer) {
var error = -1;
if (input[0] !== answer[0]) {
error = 0;
} else if (input.indexOf('hello') > input.indexOf('there')) {
error = 1;
}
return grammar_hints.quest1.task1[error];
}
}
};
return grammar_rules.Lee.quest1.task1(input, answer);
};
It would be much easier if you'd consider a more declarative approach:
- define a standard quest structure
- define a standard task structure with generic input formats
- define generic validators and re-use them
You started on the right path with the grammar_hints object, but I would actually put all the properties portraying to one task in the same object.
Suggestion:
var quests = [
{
name: 'Quest 1',
tasks: [
{
name: 'Task 1',
solution: 'hi I want to eat',
validators: [
validators.first('hi'),
validators.verbAfterNoun('want', 'I'),
]
}
],
},
];
You will be able to re-use a lot of the validators in multiple tasks so you want them to be as generic as possible, here is one example:
var validators = {
first: function (input, term) {
if (input[0] !== term) {
return 'The sentence needs to start with: ' + term;
}
},
verbAfterNoun: function (input, verb, noun) {
if (input.indexOf(verb) < input.indexOf(noun)) {
return 'The verb, ' + verb + ', needs to come after the noun ' + noun;
}
}
};
Now because you want to have a declarative format (I went with actually initializing the validators with their input and passing the result in the validators array), we would need a validator factory that takes a generic validator and returns a helper method that can be re-used with only the input. This will help us down the line so our testing framework won't need to know how many inputs to pass to each of the validator callbacks
// This is a factory method that applies the given callback (with the given arguments)
function makeValidator (fn) {
return function inputFN () {
var args = [].slice.call(arguments);
return function validate (input) {
return fn.apply(null, [input].concat(args));
}
}
}
// Apply the makeValidator() method on all the validators
for (var key in validators) {
validators[key] = makeValidator(validators[key]);
}
And finally we also want a standard way of checking our tasks against input:
// This method provides the generic validation framework for any task given any input
function validate (task, input) {
var wordList = input.split(' ');
if (input === task.solution) return {success: true, errors: []};
var errors = [];
task.validators.forEach(function (fn) {
var error = fn(wordList);
if (error) errors.push(error);
});
return {success: false, errors: errors};
}
And some examples:
var task = quests[0].tasks[0];
console.log(validate(task, 'hi I want to eat'));
console.log(validate(task, 'I want to eat hi'));
console.log(validate(task, 'hi want I to eat'));
console.log(validate(task, 'want I to eat hi'));
Putting it all together:
// This is a factory method that applies the given callback (with the given arguments)
function makeValidator (fn) {
return function inputFN () {
var args = [].slice.call(arguments);
return function validate (input) {
return fn.apply(null, [input].concat(args));
}
}
}
var validators = {
first: function (input, term) {
if (input[0] !== term) {
return 'The sentence needs to start with: ' + term;
}
},
verbAfterNoun: function (input, verb, noun) {
if (input.indexOf(verb) < input.indexOf(noun)) {
return 'The verb, ' + verb + ', needs to come after the noun ' + noun;
}
}
};
// Apply the makeValidator() method on all the validators
for (var key in validators) {
validators[key] = makeValidator(validators[key]);
}
var quests = [
{
name: 'Quest 1',
tasks: [
{
name: 'Task 1',
solution: 'hi I want to eat',
validators: [
validators.first('hi'),
validators.verbAfterNoun('want', 'I'),
]
}
],
},
];
// This method provides the generic validation framework for any task given any input
function validate (task, input) {
var wordList = input.split(' ');
if (input === task.solution) return {success: true, errors: []};
var errors = [];
task.validators.forEach(function (fn) {
var error = fn(wordList);
if (error) errors.push(error);
});
return {success: false, errors: errors};
}
function printTask (input) {
var task = quests[0].tasks[0];
var result = validate(task, input);
document.body.innerHTML += '<div><b>checking:</b> ' + input + '<pre>' + JSON.stringify(result, null, 4) + '</pre><hr />';
}
// Lets look at some examples
printTask('I want to eat hi');
printTask('hi want I to eat');
printTask('want I to eat hi');
printTask('hi I want to eat');

jQuery Validation with promises

I'm using the jQuery Validation plugin for a signup form and trying to check if an email address is already taken as part of the validation. The problem is that the check happens via a promise, and I don't know how (or if) I can make this work with the validation plugin. Here's what I have currently:
$("#signup form").validate({
rules: {
emailSignup: {
email: true,
required: true,
remote: checkAvailable
}
}
});
function checkAvailable() {
var email = $("#emailSignup").val(),
available;
App.isEmailAvailable(email)
.then(function(response) {
available = response;
})
.error(function() {
available = response;
});
setTimeout(function() {
if (available == true) {
console.log("is available");
return true;
} else {
console.log("not available");
return false;
}
}, 100);
}
The setTimeout is just a hacky way to ensure I have the response before I try to log it (I know that's terrible, but it's just for testing).
The thing is, this will correctly log is available or not available depending on the email address I enter, so it works up to that point. But I can't seem to actually return true/false back up to the remote method so that it kicks off the error handling. So what happens is anything that's a syntactically valid email gets marked as valid, even if it's logging not available based on the response.
Is this at all possible?
Update: I also tried doing this in a custom method:
jQuery.validator.addMethod("email", function(value, element) {
var field = this,
available;
App.isEmailAvailable(value)
.then(function(response) {
available = response;
})
.error(function() {
available = response;
});
setTimeout(function() {
if (available == true) {
console.log("is available");
return field.optional(element) || /^[\w-+\.]+#([\w-]+\.)+[\w-]{2,4}$/.test(value);
} else {
console.log("not available");
return false;
}
}, 100);
}, jQuery.validator.format("Please enter a valid email address."));
So the idea here is it would check if the address is available, and if it is, then it would check that it's syntactically valid. Again, it correctly logs is available/not available based on the address I enter, but doesn't return true/false properly to mark the field as valid or invalid.
Based on the "remote" jquery validate rule :
$.validator.addMethod('users_email_exists', function (value, element) {
var method = 'remote';
var previous = this.previousValue(element, method);
var validator = this;
if (!this.settings.messages[element.name]) {
this.settings.messages[element.name] = {};
}
previous.originalMessage = previous.originalMessage || this.settings.messages[element.name][method];
this.settings.messages[element.name][method] = previous.message;
var optionDataString = $.param({data: value});
if (previous.old === optionDataString) {
return previous.valid;
}
previous.old = optionDataString;
this.startRequest(element);
new Promise(function (fulfill) {
// YOUR STUFF, YOUR AJAX GET/POST REQUEST AND URL WITH PARAMS
$.get('/backend/users/ajax/filtered-users-list', {email: value})
.done(function (data) {
// YOUR STUFF TO VALIDATE DATA
// IF VALID TRUE -> validation success
// IF VALID FALSE -> validation failure
var valid = !data.length;
fulfill(valid);
})
}).then(function(valid) {
validator.settings.messages[ element.name ][ method ] = previous.originalMessage;
if ( valid ) {
submitted = validator.formSubmitted;
validator.resetInternals();
validator.toHide = validator.errorsFor( element );
validator.formSubmitted = submitted;
validator.successList.push( element );
validator.invalid[ element.name ] = false;
validator.showErrors();
} else {
errors = {};
message = validator.defaultMessage( element, { method: method, parameters: value } );
// YOUR STUFF, YOUR VALIDATION MESSAGE HERE
errors[ element.name ] = previous.message = 'EMAIL ALREADY ASSIGNED TO AN USER';
validator.invalid[ element.name ] = true;
validator.showErrors( errors );
}
previous.valid = valid;
validator.stopRequest( element, valid );
});
return "pending";
},
"EMAIL ALREADY ASSIGNED TO AN USER"
);
Then call your custom rule :
$("#signup form").validate({
rules: {
emailSignup: {
email: true,
required: true,
users_email_exists: true
}
}
});
Include this script as Promise class reference :
<!-- promise -->
<script src="https://www.promisejs.org/polyfills/promise-7.0.4.min.js"></script>
<script src="https://www.promisejs.org/polyfills/promise-done-7.0.4.min.js"></script>
The function that you've passed to setTimeout() will execute in future (a.k.a asynchronously) - after your checkAvailable() is completed. So its returning value is meaningless for the checkAvailable().
You should do rather the following:
DisableTheForm();
App.isEmailAvailable(value)
.then(function(response) {
$("#signup form").validate();
if( it is valid) {
EnableTheForm();
PostFormData();
}
})
.error(function() {
CryAsItIsNotAvailable();
EnableTheForm();
});
So to do your validation in response of positive feedback of your isEmailAvailable
You cannot use the remote method because it's looking for a URL parameter to access "remotely" via ajax().
You obviously would not call a JavaScript function with ajax(), so using remote to call a JavaScript function makes no sense.
You might be able to create a custom function using the .addMethod() method. However, you will have issues if any part of that is performed asynchronously as the custom rule will be evaluated before you have the result.

Javascript iterating through a set of properties

Hi I have created a function object that contains a set of properties.This is what I have:
function LoginModelDTO(data) {
var self = this;
self.UserName = ko.observable(data.UserName).extend({
minLength: {
params: 25,
message: "Username should have at least 25 chars"
},
required: {
message: "Username is required"
},
maxLength: {
params: 50,
message: "Username should not have more then 50 chars"
},
trackChanges: null
});
self.Password = ko.observable(data.Password).extend({
stringLength: {
params: 25,
},
required: {
message: "Password is required"
},
trackChanges: null
});
self.RememberMe = ko.observable(data.RememberMe).extend({
trackChanges: null
});
self.isValid = ko.computed(function () {
var bool = self.FirstName.isValid() &&
self.Username.isValid() &&
self.Password.isValid() &&
self.RememberMe() &&
return bool;
});
}
What I would like is to be able to find a way to iterate over each property and ask if it's valid without writing every property every time because I also have to write a similar structure like self.isValid for hasChanges , revertChanges etc.
Furthermore I will need to create other similar objects to LoginModelDTO that have around 30-35 properties.This will result in alot of code and a bigger javascript file then needed.
Is there any way I can iterate only threw the properties and check if they are valid? isValid should be skipped
eis gave you part of it in the comments, and Misters gave you a part in the answer, but here's it all together:
var allValidatablesAreValid = true;
for (var property in self)
{
if (self.hasOwnProperty(property) && self[property]["isValid"]) {
allValidatablesAreValid = allValidatablesAreValid && self[property].isValid();
}
// You can add an early bail out here:
// if (!allValidatablesAreValid) { break; }
}
WELL for..in statement can help you:
var obj = {
pro1:"hello",
pro2:function(){
//code here
},
etc:function(){
}//...
}
for(var property in obj)
{
if(obj.hasOwnProperty(property))
{
console.log(property)//display the name of the property and of course the access
}
}
And to access to the values of the property you can do this:
for(var property in obj)
{
if(obj.hasOwnProperty(property))
{
console.log(obj[property])//display the value of the property(in case that you need it)
}
}
Since the question was related to the knockout-validation library, I thought I would show how to do this using the library, itself.
self.isValid = ko.computed(function () {
return ko.validatedObservable(self).isValid()
});
See the above link for more information.

Categories

Resources