I have a situation where I am creating an unobtrusive validator that must validate that another field is required only if the validated field is not empty (and vice versa). The problem is that there are some edge cases where the other field does not re-validate, and I would like to force it to revalidate itself without causing an infinite loop.
My validation method looks like this:
$.validator.addMethod("jqiprequired", function (value, element, params) {
if (!this.optional(element) || (this.optional(params) && this.optional(element))) {
return true;
}
return false;
});
params is my other field (both are textboxes). If both are empty, it passes, if both have values, it passes. It only fails if only one has a value.
This works fine, except that if one field is empty, and another has a value, then you delete the value from the field with a value, the empty field is not revalidated (because it's value has not changed).
I tried doing this:
if (!this.optional(element) || (this.optional(params) && this.optional(element))) {
$('form').validate().element(params);
return true;
}
But this causes an infinite loop because each time it passes, it calls the other.
How can I cause the other field to validate, without itself calling the original field?
Instead of adding an attribute to each field, try adding a variable jqip_validating in the script where you are adding this validation method. Then, change your validation as follows:
var jqip_calledFromOtherValidator = false;
if (jqip_validating) {
jqip_validating = false;
jqip_calledFromOtherValidator = true;
}
if (!this.optional(element) || (this.optional(params) && this.optional(element))) {
if (!jqip_validating && !jqip_calledFromOtherValidator) {
jqip_validating = true;
$('form').validate().element(params);
}
return true;
}
In order for the other validator to be called, both conditions must be satisfied, and they can only be satisfied when the first validator invokes the second validator.
You can add a is_validating attribute to each fields so that, if it's on you skip the validation and if not, you set it to true, do your validation and then clear it.
Related
I would like to check a FlexForm field with the additional behaviour: If the entered value is not correct, it should not be possible to save the form. This is a similar behaviour as the "required" eval function, which refuses to save empty fields.
The code for evaluation already exists (but I am not adding entire code):
class UsernameEvaluation
{
public function evaluateFieldValue($value, $is_in, &$set)
{
if ($value) {
$errorCode = StudipPerson::checkUsername($value);
// if wrong username, should not be possible to save form
if ($errorCode != StudipPerson::USERNAME_ERROR_OK) {
$set = false;
}
}
return $value;
}
}
Even though invalid data is entered and I checked with a debugger that $set was set to false, the value is saved.
I was trying to check the validity of individual attributes using isValid method. It is returning true for an invalid attribute. My code is as follows:
person = Backbone.Model.extend({
defaults:{
name:"default name",
age:0
},
initialize:function(){
this.on("invalid",function(model,errors){
console.log(JSON.stringify(errors));
});
},
validate:function(attrs){
errors=[];
if(attrs.age<0){
errors.push({attribName:"age",errorMsg:"age should be grater than 0"});
}
return errors.length>0?errors:false;
}
});
var person1 = new person();
person1.set({
age:-5
});
console.log("checking validity of model:"+person1.isValid());
console.log("checking for validity of age attribute:"+person1.isValid('age'));
isValid() works fine if used to check the validity of the model as a whole and returns false. But when I try to check the age attribute i.e isValid('age') it returns true when it should return false.
isValid() is an underscore.js function, right? Doesn't it support passing an attribute to check for its validity? What am I missing here?
Short version
Model.isValid doesn't accept an attribute name as argument and has to be used on the whole model. If you don't, you're on undocumented territory and you will get weird behaviors.
To check individual attributes, you will have to set up your own mechanism.
Long version, why you get a different value
Model.isValid does in fact accept an (undocumented) options hash as its first argument and it internally forwards this hash to Model._validate via
this._validate({}, _.extend(options || {}, { validate: true }))
trying to set a validate attribute to true. But at this point, options is a string and won't be modified by _.extend. _validate looks like
_validate: function(attrs, options) {
if (!options.validate || !this.validate) return true;
// ...
}
checking if it indeed has to validate the model, options.validate is undefined and your isValid call gets back a true value.
isValid is a backbone API : http://backbonejs.org/#Model-isValid
The reason it is returning true is, the parameter accepted by isValid is an options paramter. It has to be an object.
One of the scenario you use options is :
validate: function(attrs, options) {
if(options.someSpecialCheck) {
// Perform some special checks here
} else {
// Perform some regular checks here
}
}
myModel.isValid({someSpecialCheck: true});
I have a View Model called SignUp with the EmailAddress property set like this:
[Required]
[DuplicateEmailAddressAttribute(ErrorMessage = "This email address already exists")]
public string EmailAddress { get; set; }
and the custom validator looks like this:
public class DuplicateEmailAddressAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
PestControlContext _db = new PestControlContext();
int hash = value.ToString().GetHashCode();
if (value == null)
{
return true;
}
if (_db.Users.Where(x => x.EmailAddressHash == hash).Count() > 0)
return false;
else
return true;
}
}
The problem I'm having is that if the user leaves the email address field blank on the sign up form the application is throwing a null reference exception error (I think it's looking for "" in the database and can't find it). What I don't understand is why this isn't being handled by the Required attribute - why is it jumping straight into the custom validator?
The Required attribute would have resulted in an error being added to the model state. It will not short-circuit the execution though. The framework continues to run other validators for the simple reason that all the errors about the request need to be sent out in a single shot. Ideally, you wouldn't want the service to say something is wrong to start with and when the user re-submits the request after making a correction, the service comes back and say some other thing is wrong and so on. It will be an annoyance, I guess.
The NullReferenceException is thrown because value.ToString() is called before the check against null. As you need the hash variable only after the check, you can solve this by reordering the statements:
if (value == null)
{
return true;
}
int hash = value.ToString().GetHashCode();
In addition, you could also move the PestControlContext after the check against null and use a using statement to dispose of it properly.
As also #Baldri pointed out, each validator can add Error messages and all of them are run, even if a previous one already signaled the data to be invalid. Furthermore, I'd not rely on that the validations are run in the order that you specify when marking the property with the attributes (some frameworks implement their own attribute ordering mechanism in order to assert that the order is deterministic, e.g. priorities or preceding attributes).
Therefore, I suggest reordering the code in the custom validator is the best solution.
I have a Grails domain object that looks like this:
class Product {
Boolean isDiscounted = false
Integer discountPercent = 0
static constraints = {
isDiscounted(nullable: false)
discountPercent(range:0..99)
}
I'd like to add a validator to discountPercent that will only validate if isDiscounted is true, something like this:
validator: { val, thisProduct ->
if (thisProduct.isDiscounted) {
// need to run the default validator here
thisProduct.discountPercent.validate() // not actual working code
} else {
thisProduct.discountPercent = null // reset discount percent
}
Does anyone know how I can do this?
This is more or less what you need (on the discountPercent field):
validator: { val, thisProduct ->
if (thisProduct.isDiscounted)
if (val < 0) {
return 'range.toosmall' //default code for this range constraint error
}
if (99 < val) {
return 'range.toobig' //default code for this range constraint error
} else {
return 'invalid.dependency'
}
You can't both have a special validator that relies on something else and have a special validator, as you can't run a single validator on a field (that I know of), only on single properties. But if you run a validation on this property, you'll depend on yourself and go into endless recursion. Therefore I added the range check manually. In your i18n files you can set up something like full.packet.path.FullClassName.invalid.dependency=Product not discounted.
Good luck!
I want to ensure that one of two form fields representing a boolean value is checked. But there is no appropriate constraint to do this. nullable: false does not work.
class Organisation {
Boolean selfInspecting
static constraints = {
selfInspecting(nullable: false)
}
}
How can I check whether one of the two fields is checked or not?
Perhaps the simplest approach is to use a form that ensures a value is picked. As such, creating a radio buttons rather than checkboxes is a better solution. It would directly represent your intent as well.
You can also check this in the Controller, e.g.
if (params.checkBox1 != 'on' && params.checkBox2 != 'on')
flash.error = 'At least one value must be checked.'
return ...
you can write your own custom validator.
something like
selfInspecting(validator: {val, obj -> /*test selfInspecting here*/})
EDIT -- in response to the other answer -- you can handle this on the form, but you should also handle it on the server.
ANOTHER EDIT -- It was suggested in a comment that you might want to validate one of two fields on your Domain class. This is also easily accomplished with a custom validator. With the signature above for the custom validator closure, the val is the value selfInspecting, and obj is the domain object instance. So you could have
{ val, obj ->
if (val == null) return false // if you want to ensure selfInspecting is not null
else return true
... or ...
// if you want to check that at least 1 of two fields is not null
def oneOrTheOther = false
if (obj.field1 != null || obj.field2 != null)
oneOrTheOther = true
return oneOrTheOther
}