// #FFF or #FFFFFF
color: /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/
+ },
+ validators: {
+ diceRoll: function(el, required, parent) {
+ var possibilities = [true, false];
+ return possibilities[Math.round(Math.random())];
+ }
}
}
});
```
-{{/markdown}}
\ No newline at end of file
+{{/markdown}}
***
+## Custom Validators
+
+`equalTo` is actually an example of a built-in validator. To get an idea of how validators work, check out the implementation of `equalTo`:
+
+{{#markdown}}
+```javascript
+equalTo: function(el, required, parent) {
+ var from = document.getElementById(el.getAttribute(this.add_namespace('data-equalto'))).value,
+ to = el.value,
+ valid = (from === to);
+
+ return valid;
+}
+```
+{{/markdown}}
+
+Your function will be passed three arguments: `el`, `required`, and `parent`. These are the elements being validated, whether or not it was marked as required, and the parent of the element being validated, respectively.
+Your function should return `true` if the element is valid, or `false` if it is invalid. You can define your custom validators and pass them into abide options when initializing foundation. See the Configuration section for more details on defining custom validators.
+
+Suppose you've defined a custom validator `diceRoll` which randomly marks your element as valid or invalid. You would use it like so:
+
+{{#markdown}}
+```html
+<input type="text" data-abide-validator="diceRoll">
+```
+{{/markdown}}
+
+***
+
## Using the Javascript
<div class="panel">
// #FFF or #FFFFFF
color: /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/
+ },
+ validators : {
+ equalTo: function(el, required, parent) {
+ var from = document.getElementById(el.getAttribute(this.add_namespace('data-equalto'))).value,
+ to = el.value,
+ valid = (from === to);
+
+ return valid;
+ }
}
},
} else if (pattern.length > 0) {
return [el, new RegExp(pattern), required];
}
-
+
if (this.settings.patterns.hasOwnProperty(type)) {
return [el, this.settings.patterns[type], required];
}
required = el_patterns[i][2],
value = el.value,
direct_parent = this.S(el).parent(),
- is_equal = el.getAttribute(this.add_namespace('data-equalto')),
+ validator = el.getAttribute(this.add_namespace('data-abide-validator')),
is_radio = el.type === "radio",
is_checkbox = el.type === "checkbox",
label = this.S('label[for="' + el.getAttribute('id') + '"]'),
valid_length = (required) ? (el.value.length > 0) : true;
- var parent;
+ var parent, valid;
+
+ // support old way to do equalTo validations
+ if(el.getAttribute(this.add_namespace('data-equalto'))) { validator = "equalTo" }
if (!direct_parent.is('label')) {
parent = direct_parent;
validations.push(this.valid_radio(el, required));
} else if (is_checkbox && required) {
validations.push(this.valid_checkbox(el, required));
- } else if (is_equal && required) {
- validations.push(this.valid_equal(el, required, parent));
+ } else if (validator) {
+ valid = this.settings.validators[validator].apply(this, [el, required, parent])
+ validations.push(valid);
+
+ if (valid) {
+ this.S(el).removeAttr(this.invalid_attr);
+ parent.removeClass('error');
+ } else {
+ this.S(el).attr(this.invalid_attr, '');
+ parent.addClass('error');
+ }
} else {
if (el_patterns[i][1].test(value) && valid_length ||
}
}
- return valid;
- },
-
- valid_equal: function(el, required, parent) {
- var from = document.getElementById(el.getAttribute(this.add_namespace('data-equalto'))).value,
- to = el.value,
- valid = (from === to);
-
- if (valid) {
- this.S(el).removeAttr(this.invalid_attr);
- parent.removeClass('error');
- } else {
- this.S(el).attr(this.invalid_attr, '');
- parent.addClass('error');
- }
-
return valid;
}
};
expect('invalid').not.toHaveBeenTriggeredOn('input[name="user_email"]');
});
});
+
+ describe('advanced validation', function() {
+ beforeEach(function() {
+ document.body.innerHTML = __html__['spec/abide/advanced.html'];
+ });
+
+ it('should support builtin equalTo validator', function() {
+ $(document).foundation({
+ abide: {
+ validators: {
+ range: function(){ return true; }
+ }
+ }
+ });
+
+
+ expect($('input[name="user_password"]')).not.toHaveAttr('data-invalid');
+ expect($('input[name="user_password_confirmation"]')).not.toHaveAttr('data-invalid');
+
+ $('input[name="user_password"]').val("foobarbaz");
+ // now they're not equal
+ $('form').submit();
+
+ var invalid_fields = $('form').find('[data-invalid]');
+ expect(invalid_fields.length).toBe(1);
+ expect($('input[name="user_password_confirmation"]')).toHaveAttr('data-invalid');
+
+ $('input[name="user_password_confirmation"]').val("foobarbaz");
+ // now they're equal
+ spyOnEvent('form', 'invalid');
+ spyOnEvent('form', 'valid');
+
+ $('form').submit();
+
+ expect('valid').toHaveBeenTriggeredOn('form');
+ expect($('input[name="user_password"]')).not.toHaveAttr('data-invalid');
+ expect($('input[name="user_password_confirmation"]')).not.toHaveAttr('data-invalid');
+ });
+
+ it('should support custom validators', function() {
+ $(document).foundation({
+ abide: {
+ validators: {
+ range: function(el, required, parent) {
+ var start = parseInt(this.S("[name='user_start_num']").val()),
+ end = parseInt(el.value);
+
+ return start < end;
+ }
+ }
+ }
+ });
+
+ expect($('input[name="user_start_num"]')).not.toHaveData('invalid');
+ expect($('input[name="user_end_num"]')).not.toHaveData('invalid');
+
+ // invalid
+ $('input[name="user_start_num"]').val("10");
+ $('input[name="user_end_num"]').val("2");
+
+ $('form').submit();
+
+ var invalid_fields = $('form').find('[data-invalid]');
+ expect(invalid_fields.length).toBe(1);
+
+ expect($('input[name="user_start_num"]')).not.toHaveAttr('data-invalid');
+ expect($('input[name="user_end_num"]')).toHaveAttr('data-invalid');
+
+ // valid now
+ $('input[name="user_end_num"]').val("12");
+ spyOnEvent('form', 'invalid');
+ spyOnEvent('form', 'valid');
+
+ $('form').submit();
+
+ expect('valid').toHaveBeenTriggeredOn('form');
+ expect($('input[name="user_start_num"]')).not.toHaveAttr('data-invalid');
+ expect($('input[name="user_end_num"]')).not.toHaveAttr('data-invalid');
+ });
+ });
});
--- /dev/null
+<form data-abide="ajax">
+ <div class="range-start-field">
+ <label>Starting number <small>required</small></label>
+ <input name="user_start_num" type="text">
+ <small class="error">Starting number must be before the end date</small>
+ </div>
+ <div class="range-end-field">
+ <label>Ending number <small>required</small></label>
+ <input name="user_end_num" type="text" data-abide-validator="range">
+ <small class="error">Ending number must be before the end date</small>
+ </div>
+ <div class="password-field">
+ <label>Password <small>required</small></label>
+ <input id="user_password" name="user_password" type="password">
+ </div>
+ <div class="password-confirmation-field">
+ <label>Password confirmation<small>required</small></label>
+ <input name="user_password_confirmation" type="password" data-abide-validator="equalTo" data-equalto="user_password">
+ <small class="error">Password and password confirmation must match.</small>
+ </div>
+ <button type="submit">Submit</button>
+</form>