Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add Parsley.js validator with referenced field

I want to add a couple of checks for my forms whose condition is related to other fields' values (i.e. I have a range form and from field must be less than to field, and viceversa). I didn't find anything like that on the present Validators so I tried to add those myself.

So, I added these two functions to Assert.prototype:

GreaterThanReference: function ( reference ) {
    this.__class__ = 'GreaterThanReference';
    if ( 'undefined' === typeof reference )
        throw new Error( 'GreaterThanReference must be instanciated with a value or a function' );
    this.reference = reference;
    this.validate = function ( value ) {
        var reference = 'function' === typeof this.reference ? this.reference( value ) : this.reference;
        if ( '' === value || isNaN( Number( value ) ) )
            throw new Violation( this, value, { value: Validator.errorCode.must_be_a_number } );
        if ( this.reference.value >= value )
            throw new Violation( this, value );
        return true;
    };
    return this;
}

and

LessThanReference: function ( reference ) {
    this.__class__ = 'LessThanReference';
    if ( 'undefined' === typeof reference )
        throw new Error( 'LessThanReference must be instanciated with a value or a function' );
    this.reference = reference;
    this.validate = function ( value ) {
        var reference = 'function' === typeof this.reference ? this.reference( value ) : this.reference;
        if ( '' === value || isNaN( Number( value ) ) )
            throw new Violation( this, value, { value: Validator.errorCode.must_be_a_number } );
        if ( this.reference.value <= value )
            throw new Violation( this, value );
        return true;
    };
    return this;
}

and these other two to ParsleyValidator.prototype.validators:

greaterthan: function (value) {
    return $.extend(new Validator.Assert().GreaterThanReference(value), {
        priority: 256,
        requirementsTransformer: function () {
        return { name : $(value).attr('alt'), value : +$(value).val() };
    }});
}

and

lessthan: function (value) {
    return $.extend(new Validator.Assert().LessThanReference(value), {
        priority: 256,
        requirementsTransformer: function () {
        return { name : $(value).attr('alt'), value : +$(value).val() };
    }});
}

Then I wanted to add the revesed check on the referenced field, so that if I change only the referenced field's value I can still validate the form (in the range example, if I change from value, I should validate to field. And if I change to value, I should validate from field). To achieve that, I edited ParsleyField.prototype.addConstraint:

addConstraint: function (name, requirements, priority, isDomConstraint, isReference) {
    name = name.toLowerCase();
    if ('function' === typeof window.ParsleyValidator.validators[name]) {
        var constraint = new ConstraintFactory(this, name, requirements, priority, isDomConstraint);
        // if constraint is a referenced one, I add the specular constraint on the referenced field
        if((name == 'lessthan' || name == 'greaterthan') && isReference !== true) {
            // I check if I already instanciated the referenced ParsleyField
            var referencedField = $(requirements).data('Parsley') && $(requirements).data('Parsley').__class__ == 'ParsleyField' ?
                $(requirements).data('Parsley') :
                new ParsleyField($(requirements), this.parsleyInstance).init();
            referencedField.addConstraint(name == 'lessthan' ? 'greaterthan' : 'lessthan', '#' + this.$element.attr('id'), priority, isDomConstraint, true);
        }
        // if constraint already exist, delete it and push new version
        if ('undefined' !== this.constraintsByName[constraint.name])
            this.removeConstraint(constraint.name);
        this.constraints.push(constraint);
        this.constraintsByName[constraint.name] = constraint;
    }
    return this;
}

In detail, I added the isReference argument to know if I already had added the reverse constraint on the referencing field, to avoid circular references. Then, in a quite horrible way, I check if the constraint I'm adding has a reference and isn't already a referenced constraint (maybe it can be improved by adding some sort of "constraint type" which could be indirect (or referenced) for the constraints who check other fields, and direct for those who just check the value). If this condition is true, I have to add the new constraint to the already instanciated ParsleyField, or if has not been instanciated, to a new ParsleyField.

This method has a problem. If I add a constraint on a field that is referencing a field that has not been instanciated yet, when the normal flow of Parsley gets to that field it overwrites it, eliminating the constraint I added before.

Zero-question: is this the right way to achieve what I want? I admit I didn't explore Parsley API too much, but I'd like to use as less features as possible, as I'm using the same checks server side and I should be able to do the same things on both sides.

If zero-answer is 'yes', how can I solve that overwriting problem? Probably I should be able on ParsleyField instantiation to check if it has already been instanciated, but how?

like image 488
goffreder Avatar asked Nov 11 '22 10:11

goffreder


1 Answers

I don't know which version of parsley js you are using and maybe my answer is not actual now. However, I faced the same issue with v.2.0.2. Fortunately, docs contain a tutorial for writing custom validators. Could you please check the following ones https://github.com/mvpotter/parsley-extra-validators . If I understand you correctly, they do exactly what you need. Any feedback is appreciated.

like image 77
Michael Potter Avatar answered Nov 15 '22 04:11

Michael Potter