One of the first things that a YUI developer learns about are the attribute change events that are fired any time an attribute value is successfully changed. But one often overlooked feature of Y.Attribute is the validator.

The validator allows you to test that a value is valid for the attribute prior to it being sent to the setter. A common use is validating that the value trying to be set is in an accepted format - such as only allowing integers in a quantity attribute. Returning true from the validator sends the value to the setter, returning false causes the value to not be set. But unlike a successful change event no event is fired to notify you that the value failed validation and was not set. So lets augment our modules to fire a validation failed event.

Unfortunately to monkey patch this feature into Y.Attribute properly would require a large amount of code duplication so we’ll leave that to the coming pull request. Instead we are going to pick a small simple method which will allow us to inject our enhancement but not force us to duplicate a bunch of the library code. This is only intended to be a short term solution until the patch makes its way into the library as monkey patching introduces a number of potential negative side effects.

Because we may not want to apply this patch to all of the modules which extend Y.Attribute we will only modify this method in the objects that we need it for. To make this really easy we will create a small object and mix it into the instances.

Y.attrValidatePatch = function() {};

Y.attrValidatePatch.prototype = {
    _defAttrChangeFn: function(e) {
        if (!this._setAttrVal(e.attrName, e.subAttrName, e.prevVal, e.newVal)) {
            e.stopImmediatePropagation();
            this.fire(e.attrName+'ValidationFail', e); // The only new line
        } else {
            e.newVal = this.get(e.attrName);
        }
    }
};

We then mix that patch into our new module

var CustomModule = Y.Base.create('custom-module', Y.Base, [Y.attrValidatePatch], {

    //Custom  module code here

}, {
    ATTRS: {
        "number": {
            validator: function(val) {
                return Y.Lang.isNumber(val);
            }
        }
    }
});

And now we are able to listen for that attributes validation fail event just like we would for the change event

var MyModule = new CustomModule();

MyModule.on('numberValidationFail', function(e) {
    Y.log('Validation failed setting ' + e.attrName + ' to ' + e.newVal, 'error', MyModule.name);
});

// As usual you need to listen using 'after' to be sure attributes were set
MyModule.after('numberChange', function(e) { Y.log('New value is: ' + e.newVal); });

MyModule.set('number', 100); // Valid value
MyModule.set('number', 'to string'); //Invalid value

Wrap all of the above code up in a use() and watch your console to see it in action. As usual If you are having any issues implementing this patch feel free to comment below, mention me @fromanegg or pop into #yui on irc.freenode.net