Browse Source

Use Bootstrap help block element instead of tooltip to show the errors

phuoc 12 years ago
parent
commit
2a3f3100de

+ 1 - 5
README.md

@@ -37,10 +37,6 @@ Call the plugin to validate the form as following:
 $(document).ready(function() {
     $(<form Selector>).bootstrapValidate({
         message: <The default error message for all fields>,
-        iconClass: {
-            valid: <The icon class indicates a valid value>,
-            invalid: <The icon class indicates an invalid value>
-        },
         fields: {
             ...
             <fieldName>: {
@@ -65,7 +61,7 @@ Below is the list of built-in validators sorted in alphabetical order:
 
 Validator name                | Description
 ------------------------------|------------
-[between](#between-validator) | Checks if the input value is between (strictly or not) two given numbers
+[between](#between-validator) | Check if the input value is between (strictly or not) two given numbers
 digits                        | Return true if the value contains only digits
 emailAddress                  | Validate an email address
 greaterThan                   | Return true if the value is greater than or equals to given number

+ 1 - 5
demo/index.html

@@ -67,6 +67,7 @@
 <script type="text/javascript">
 $(document).ready(function() {
     $('#defaultForm').bootstrapValidate({
+        message: 'This value is not valid',
         fields: {
             username: {
                 message: 'The username is not valid',
@@ -117,11 +118,6 @@ $(document).ready(function() {
                     }
                 }
             }
-        },
-        message: 'This value is not valid',
-        iconClass: {
-            valid: 'icon-ok',
-            invalid: 'icon-remove'
         }
     });
 });

+ 7 - 6
demo/remote.html

@@ -9,6 +9,8 @@
     <![endif]-->
     <link rel="stylesheet" href="/vendor/font-awesome/css/font-awesome.min.css"/>
 
+    <link rel="stylesheet" href="/dist/css/bootstrapvalidate.css"/>
+
     <script type="text/javascript" src="/vendor/jquery/jquery-1.10.2.min.js"></script>
     <script type="text/javascript" src="/vendor/bootstrap/js/bootstrap.min.js"></script>
     <script type="text/javascript" src="/dist/js/bootstrapvalidate.js"></script>
@@ -23,7 +25,7 @@
                 </div>
 
                 <div class="col-lg-8 col-lg-offset-2">
-                    <form id="defaultForm" method="post" class="form-horizontal">
+                    <form id="defaultForm" method="post" action="remote.html" class="form-horizontal">
                         <fieldset>
                             <legend>Remote validator</legend>
 
@@ -57,6 +59,7 @@
 <script type="text/javascript">
 $(document).ready(function() {
     $('#defaultForm').bootstrapValidate({
+        message: 'This value is not valid',
         fields: {
             username: {
                 message: 'The username is not valid',
@@ -75,17 +78,15 @@ $(document).ready(function() {
                     notEmpty: {
                         message: 'The email address is required and can\'t be empty'
                     },
+                    emailAddress: {
+                        message: 'The input is not a valid email address'
+                    },
                     remote: {
                         url: 'remote.php',
                         message: 'The email is not available'
                     }
                 }
             }
-        },
-        message: 'This value is not valid',
-        iconClass: {
-            valid: 'icon-ok',
-            invalid: 'icon-remove'
         }
     });
 });

+ 3 - 6
demo/validators.html

@@ -8,6 +8,7 @@
     <link rel="stylesheet" href="../vendor/font-awesome/css/font-awesome-ie7.min.css">
     <![endif]-->
     <link rel="stylesheet" href="../vendor/font-awesome/css/font-awesome.min.css"/>
+    <link rel="stylesheet" href="../dist/css/bootstrapvalidate.css"/>
 
     <script type="text/javascript" src="../vendor/jquery/jquery-1.10.2.min.js"></script>
     <script type="text/javascript" src="../vendor/bootstrap/js/bootstrap.min.js"></script>
@@ -23,7 +24,7 @@
                 </div>
 
                 <div class="col-lg-8 col-lg-offset-2">
-                    <form id="defaultForm" method="post" class="form-horizontal">
+                    <form id="defaultForm" method="post" action="validators.html" class="form-horizontal">
                         <fieldset>
                             <legend>Not Empty validator</legend>
 
@@ -126,6 +127,7 @@
 <script type="text/javascript">
 $(document).ready(function() {
     $('#defaultForm').bootstrapValidate({
+        message: 'This value is not valid',
         fields: {
             username: {
                 message: 'The username is not valid',
@@ -225,11 +227,6 @@ $(document).ready(function() {
                     }
                 }
             }
-        },
-        message: 'This value is not valid',
-        iconClass: {
-            valid: 'icon-ok',
-            invalid: 'icon-remove'
         }
     });
 });

+ 11 - 0
dist/css/bootstrapvalidate.css

@@ -0,0 +1,11 @@
+/**
+ * BootstrapValidate (https://github.com/nghuuphuoc/bootstrapvalidate)
+ *
+ * A jQuery plugin to validate form fields. Use with Bootstrap 3
+ *
+ * @author      Nguyen Huu Phuoc <phuoc@huuphuoc.me>
+ * @copyright   (c) 2013 Nguyen Huu Phuoc
+ * @license     MIT
+ */
+.bootstrap-validate-form .help-block {
+  margin-bottom: 0; }

+ 12 - 0
dist/css/bootstrapvalidate.min.css

@@ -0,0 +1,12 @@
+/**
+ * BootstrapValidate v0.1.0 (http://github.com/nghuuphuoc/bootstrapvalidate)
+ *
+ * A jQuery plugin to validate form fields. Use with Bootstrap 3
+ *
+ * @author      Nguyen Huu Phuoc <phuoc@huuphuoc.me>
+ * @copyright   (c) 2013 Nguyen Huu Phuoc
+ * @license     MIT
+ */
+
+
+.bootstrap-validate-form .help-block{margin-bottom:0}

+ 15 - 0
dist/css/bootstrapvalidate.scss

@@ -0,0 +1,15 @@
+/**
+ * BootstrapValidate (https://github.com/nghuuphuoc/bootstrapvalidate)
+ *
+ * A jQuery plugin to validate form fields. Use with Bootstrap 3
+ *
+ * @author      Nguyen Huu Phuoc <phuoc@huuphuoc.me>
+ * @copyright   (c) 2013 Nguyen Huu Phuoc
+ * @license     MIT
+ */
+
+.bootstrap-validate-form {
+  .help-block {
+    margin-bottom: 0;
+  }
+}

+ 104 - 86
dist/js/bootstrapvalidate.js

@@ -22,10 +22,14 @@
     $.bootstrapValidator = function(form, options) {
         this.$form   = $(form);
         this.options = $.extend({}, $.bootstrapValidator.DEFAULT_OPTIONS, options);
-        this.validate();
-        this.xhrRequests = {};
+
+        this.invalidFields      = {};
+        this.xhrRequests        = {};
         this.numPendingRequests = 0;
+
+        this.init();
     };
+
     $.extend($.bootstrapValidator, {
         /**
          * The default options
@@ -35,13 +39,7 @@
             message: 'This value is not valid',
 
             // Map the field name with validator rules
-            fields: null,
-
-            // CSS class of icons indicating that the field value is valid or not
-            iconClass: {
-                valid: 'icon-ok',
-                invalid: 'icon-remove'
-            }
+            fields: null
         },
 
         // Available validators
@@ -59,16 +57,31 @@
             /**
              * Validate form
              */
-            validate: function() {
+            init: function() {
                 if (this.options.fields == null) {
                     return;
                 }
+
+                var that = this;
+                this.$form
+                    .addClass('bootstrap-validate-form')
+                    .on('submit', function(e) {
+                        if (that.options.fields) {
+                            for (var field in that.options.fields) {
+                                that.validateField(field);
+                            }
+                            if (!that.isValid()) {
+                                e.preventDefault();
+                            }
+                        }
+                    });
+
                 for (var field in this.options.fields) {
-                    this.validateField(field);
+                    this.initField(field);
                 }
             },
 
-            validateField: function(field) {
+            initField: function(field) {
                 if (this.options.fields[field] == null || this.options.fields[field].validator == null) {
                     return;
                 }
@@ -78,97 +91,89 @@
                     return;
                 }
 
-                var that   = this,
-                    $field = $(foundFields[0]),
-                    type   = $field.attr('type'),
-                    event  = ('checkbox' == type || 'radio' == type) ? 'change' : 'keyup';
+                // Create a help block element for showing the error
+                var that      = this,
+                    $field    = $(foundFields[0]),
+                    $parent   = $field.parents('.form-group'),
+                    helpBlock = $parent.find('.help-block');
+
+                if (helpBlock.length == 0) {
+                    var $small = $('<small/>').addClass('help-block').appendTo($parent);
+                    $field.data('bootstrapValidator.error', $small);
 
-                $field
-                    .on(event, function() {
-                        var validators = that.options.fields[field].validator;
-                        for (var validatorName in validators) {
-                            if (!$.bootstrapValidator.validator[validatorName]) {
-                                continue;
+                    // Calculate the number of columns of the label/field element
+                    // Then set offset to the help block element
+                    var label, cssClasses, offset;
+                    if (label = $parent.find('label').get(0)) {
+                        cssClasses = $(label).attr('class').split(' ');
+                        for (var i = 0; i < cssClasses.length; i++) {
+                            if (cssClasses[i].substr(0, 7) == 'col-lg-') {
+                                offset = cssClasses[i].substr(7);
+                                break;
                             }
-                            var isValid = $.bootstrapValidator.validator[validatorName].validate(that, $field, validators[validatorName]);
-                            if (isValid === false) {
-                                that.showError($field, validatorName);
+                        }
+                    } else {
+                        cssClasses = $parent.children().eq(0).attr('class').split(' ');
+                        for (var i = 0; i < cssClasses.length; i++) {
+                            if (cssClasses[i].substr(0, 14) == 'col-lg-offset-') {
+                                offset = cssClasses[i].substr(14);
                                 break;
-                            } else if (isValid === true) {
-                                that.removeError($field);
                             }
                         }
-                    })
-                    .blur(function() {
-                        that.hideError($field);
-                    });
+                    }
+                    $small.addClass('col-lg-offset-' + offset).addClass('col-lg-' + parseInt(12 - offset));
+                } else {
+                    $field.data('bootstrapValidator.error', helpBlock.eq(0));
+                }
+
+                var type  = $field.attr('type'),
+                    event = ('checkbox' == type || 'radio' == type) ? 'change' : 'keyup';
+                $field.on(event, function() {
+                    that.validateField(field);
+                });
+            },
+
+            validateField: function(field) {
+                var foundFields = this.$form.find('[name="' + field + '"]');
+                if (foundFields.length == 0) {
+                    // Return if cannot find the field with given name
+                    return;
+                }
+                var that       = this,
+                    $field     = $(foundFields[0]),
+                    validators = that.options.fields[field].validator;
+                for (var validatorName in validators) {
+                    if (!$.bootstrapValidator.validator[validatorName]) {
+                        continue;
+                    }
+                    var isValid = $.bootstrapValidator.validator[validatorName].validate(that, $field, validators[validatorName]);
+                    if (isValid === false) {
+                        that.showError($field, validatorName);
+                        break;
+                    } else if (isValid === true) {
+                        that.removeError($field);
+                    }
+                }
             },
 
             showError: function($field, validatorName) {
                 var field     = $field.attr('name'),
                     validator = this.options.fields[field].validator[validatorName],
-                    message   = validator.message || this.options.message;
-
-                if (!$field.data('bootstrapValidator.tooltip')) {
-                    var $a = $('<a/>').attr('href', '#')
-                                      .attr('title', message)
-                                      // Bootstrap tooltip options
-                                      // see http://getbootstrap.com/javascript/#tooltips
-                                      .attr('data-toggle', 'tooltip').attr('data-placement', 'right')
-                                      .css('text-decoration', 'none')
-                                      .css('position', 'absolute')
-                                      .insertAfter($field);
-                    $('<i/>').addClass(this.options.iconClass.invalid).appendTo($a);
-                    $field.data('bootstrapValidator.tooltip', $a);
+                    message   = validator.message || this.options.message,
+                    $parent   = $field.parents('.form-group');
 
-                    $a.on('shown.bs.tooltip', function() {
-                        if (!$(this).data('bootstrapValidator.tooltip.calculated')) {
-                            $(this).data('bootstrapValidator.tooltip.calculated', true);
-                            var $parent   = $(this).parent(),
-                                $tip      = $(this).data('bs.tooltip').$tip,
-                                w         = $parent.width(),
-                                h         = $parent.height(),
-                                tipWidth  = parseInt($tip.width()),
-                                tipHeight = parseInt($tip.height()),
-                                tipLeft   = parseInt($tip.css('left')),
-                                tipTop    = parseInt($tip.css('top'));
-                            $tip.css('left', tipLeft + w + 10)
-                                .css('top', tipTop - h + 5)
-                                .width(tipWidth);
-                            $(this).css('position', 'absolute')
-                                   .css('left', tipLeft - $(this).width() + w + 5)
-                                   .css('top', tipTop + tipHeight / 2 - $(this).height() / 2 - h + 5);
-                        }
-                    });
-                }
+                this.invalidFields[field] = true;
 
                 // Add has-error class to parent element
-                $field.parents('.form-group').removeClass('has-success').addClass('has-error');
-
-                $field
-                    .data('bootstrapValidator.tooltip')
-                        .find('i').attr('class', this.options.iconClass.invalid).end()
-                        .attr('title', message)
-                        .attr('data-original-title', message)
-                        .tooltip('show');
-            },
+                $parent.removeClass('has-success').addClass('has-error');
 
-            hideError: function($field) {
-                var $tip = $field.data('bootstrapValidator.tooltip');
-                if ($tip) {
-                    $tip.tooltip('hide');
-                }
+                $field.data('bootstrapValidator.error').html(message).show();
             },
 
             removeError: function($field) {
+                this.invalidFields[$field.attr('name')] = false;
                 $field.parents('.form-group').removeClass('has-error').addClass('has-success');
-                var $tip = $field.data('bootstrapValidator.tooltip');
-                if ($tip) {
-                    $tip.find('i').attr('class', this.options.iconClass.valid).end()
-                        .tooltip('destroy')
-                        .remove();
-                    $field.removeData('bootstrapValidator.tooltip');
-                }
+                $field.data('bootstrapValidator.error').hide();
             },
 
             startRequest: function($field, validatorName, xhr) {
@@ -192,10 +197,23 @@
                     return;
                 }
 
-                var xhr = this.xhrRequests[field][validatorName];
                 this.numPendingRequests--;
+                var xhr = this.xhrRequests[field][validatorName];
                 xhr.abort();
                 delete this.xhrRequests[field][validatorName];
+            },
+
+            isValid: function() {
+                console.log(this.numPendingRequests);
+                if (this.numPendingRequests > 0) {
+                    return false;
+                }
+                for (var field in this.invalidFields) {
+                    if (this.invalidFields[field]) {
+                        return false;
+                    }
+                }
+                return true;
             }
         }
     });

File diff suppressed because it is too large
+ 1 - 1
dist/js/bootstrapvalidate.min.js


+ 11 - 0
src/css/bootstrapvalidate.css

@@ -0,0 +1,11 @@
+/**
+ * BootstrapValidate (https://github.com/nghuuphuoc/bootstrapvalidate)
+ *
+ * A jQuery plugin to validate form fields. Use with Bootstrap 3
+ *
+ * @author      Nguyen Huu Phuoc <phuoc@huuphuoc.me>
+ * @copyright   (c) 2013 Nguyen Huu Phuoc
+ * @license     MIT
+ */
+.bootstrap-validate-form .help-block {
+  margin-bottom: 0; }

+ 15 - 0
src/css/bootstrapvalidate.scss

@@ -0,0 +1,15 @@
+/**
+ * BootstrapValidate (https://github.com/nghuuphuoc/bootstrapvalidate)
+ *
+ * A jQuery plugin to validate form fields. Use with Bootstrap 3
+ *
+ * @author      Nguyen Huu Phuoc <phuoc@huuphuoc.me>
+ * @copyright   (c) 2013 Nguyen Huu Phuoc
+ * @license     MIT
+ */
+
+.bootstrap-validate-form {
+  .help-block {
+    margin-bottom: 0;
+  }
+}

+ 106 - 88
src/js/bootstrapvalidate.js

@@ -22,10 +22,14 @@
     $.bootstrapValidator = function(form, options) {
         this.$form   = $(form);
         this.options = $.extend({}, $.bootstrapValidator.DEFAULT_OPTIONS, options);
-        this.validate();
-        this.xhrRequests = {};
+
+        this.invalidFields      = {};
+        this.xhrRequests        = {};
         this.numPendingRequests = 0;
+
+        this.init();
     };
+
     $.extend($.bootstrapValidator, {
         /**
          * The default options
@@ -35,13 +39,7 @@
             message: 'This value is not valid',
 
             // Map the field name with validator rules
-            fields: null,
-
-            // CSS class of icons indicating that the field value is valid or not
-            iconClass: {
-                valid: 'icon-ok',
-                invalid: 'icon-remove'
-            }
+            fields: null
         },
 
         // Available validators
@@ -59,16 +57,31 @@
             /**
              * Validate form
              */
-            validate: function() {
+            init: function() {
                 if (this.options.fields == null) {
                     return;
                 }
+
+                var that = this;
+                this.$form
+                    .addClass('bootstrap-validate-form')
+                    .on('submit', function(e) {
+                        if (that.options.fields) {
+                            for (var field in that.options.fields) {
+                                that.validateField(field);
+                            }
+                            if (!that.isValid()) {
+                                e.preventDefault();
+                            }
+                        }
+                    });
+
                 for (var field in this.options.fields) {
-                    this.validateField(field);
+                    this.initField(field);
                 }
             },
 
-            validateField: function(field) {
+            initField: function(field) {
                 if (this.options.fields[field] == null || this.options.fields[field].validator == null) {
                     return;
                 }
@@ -78,97 +91,89 @@
                     return;
                 }
 
-                var that   = this,
-                    $field = $(foundFields[0]),
-                    type   = $field.attr('type'),
-                    event  = ('checkbox' == type || 'radio' == type) ? 'change' : 'keyup';
-
-                $field
-                    .on(event, function() {
-                        var validators = that.options.fields[field].validator;
-                        for (var validatorName in validators) {
-                            if (!$.bootstrapValidator.validator[validatorName]) {
-                                continue;
+                // Create a help block element for showing the error
+                var that      = this,
+                    $field    = $(foundFields[0]),
+                    $parent   = $field.parents('.form-group'),
+                    helpBlock = $parent.find('.help-block');
+
+                if (helpBlock.length == 0) {
+                    var $small = $('<small/>').addClass('help-block').appendTo($parent);
+                    $field.data('bootstrapValidator.error', $small);
+
+                    // Calculate the number of columns of the label/field element
+                    // Then set offset to the help block element
+                    var label, cssClasses, offset;
+                    if (label = $parent.find('label').get(0)) {
+                        cssClasses = $(label).attr('class').split(' ');
+                        for (var i = 0; i < cssClasses.length; i++) {
+                            if (cssClasses[i].substr(0, 7) == 'col-lg-') {
+                                offset = cssClasses[i].substr(7);
+                                break;
                             }
-                            var isValid = $.bootstrapValidator.validator[validatorName].validate(that, $field, validators[validatorName]);
-                            if (isValid === false) {
-                                that.showError($field, validatorName);
+                        }
+                    } else {
+                        cssClasses = $parent.children().eq(0).attr('class').split(' ');
+                        for (var i = 0; i < cssClasses.length; i++) {
+                            if (cssClasses[i].substr(0, 14) == 'col-lg-offset-') {
+                                offset = cssClasses[i].substr(14);
                                 break;
-                            } else if (isValid === true) {
-                                that.removeError($field);
                             }
                         }
-                    })
-                    .blur(function() {
-                        that.hideError($field);
-                    });
+                    }
+                    $small.addClass('col-lg-offset-' + offset).addClass('col-lg-' + parseInt(12 - offset));
+                } else {
+                    $field.data('bootstrapValidator.error', helpBlock.eq(0));
+                }
+
+                var type  = $field.attr('type'),
+                    event = ('checkbox' == type || 'radio' == type) ? 'change' : 'keyup';
+                $field.on(event, function() {
+                    that.validateField(field);
+                });
+            },
+
+            validateField: function(field) {
+                var foundFields = this.$form.find('[name="' + field + '"]');
+                if (foundFields.length == 0) {
+                    // Return if cannot find the field with given name
+                    return;
+                }
+                var that       = this,
+                    $field     = $(foundFields[0]),
+                    validators = that.options.fields[field].validator;
+                for (var validatorName in validators) {
+                    if (!$.bootstrapValidator.validator[validatorName]) {
+                        continue;
+                    }
+                    var isValid = $.bootstrapValidator.validator[validatorName].validate(that, $field, validators[validatorName]);
+                    if (isValid === false) {
+                        that.showError($field, validatorName);
+                        break;
+                    } else if (isValid === true) {
+                        that.removeError($field);
+                    }
+                }
             },
 
             showError: function($field, validatorName) {
                 var field     = $field.attr('name'),
                     validator = this.options.fields[field].validator[validatorName],
-                    message   = validator.message || this.options.message;
-
-                if (!$field.data('bootstrapValidator.tooltip')) {
-                    var $a = $('<a/>').attr('href', '#')
-                                      .attr('title', message)
-                                      // Bootstrap tooltip options
-                                      // see http://getbootstrap.com/javascript/#tooltips
-                                      .attr('data-toggle', 'tooltip').attr('data-placement', 'right')
-                                      .css('text-decoration', 'none')
-                                      .css('position', 'absolute')
-                                      .insertAfter($field);
-                    $('<i/>').addClass(this.options.iconClass.invalid).appendTo($a);
-                    $field.data('bootstrapValidator.tooltip', $a);
-
-                    $a.on('shown.bs.tooltip', function() {
-                        if (!$(this).data('bootstrapValidator.tooltip.calculated')) {
-                            $(this).data('bootstrapValidator.tooltip.calculated', true);
-                            var $parent   = $(this).parent(),
-                                $tip      = $(this).data('bs.tooltip').$tip,
-                                w         = $parent.width(),
-                                h         = $parent.height(),
-                                tipWidth  = parseInt($tip.width()),
-                                tipHeight = parseInt($tip.height()),
-                                tipLeft   = parseInt($tip.css('left')),
-                                tipTop    = parseInt($tip.css('top'));
-                            $tip.css('left', tipLeft + w + 10)
-                                .css('top', tipTop - h + 5)
-                                .width(tipWidth);
-                            $(this).css('position', 'absolute')
-                                   .css('left', tipLeft - $(this).width() + w + 5)
-                                   .css('top', tipTop + tipHeight / 2 - $(this).height() / 2 - h + 5);
-                        }
-                    });
-                }
+                    message   = validator.message || this.options.message,
+                    $parent   = $field.parents('.form-group');
+
+                this.invalidFields[field] = true;
 
                 // Add has-error class to parent element
-                $field.parents('.form-group').removeClass('has-success').addClass('has-error');
-
-                $field
-                    .data('bootstrapValidator.tooltip')
-                        .find('i').attr('class', this.options.iconClass.invalid).end()
-                        .attr('title', message)
-                        .attr('data-original-title', message)
-                        .tooltip('show');
-            },
+                $parent.removeClass('has-success').addClass('has-error');
 
-            hideError: function($field) {
-                var $tip = $field.data('bootstrapValidator.tooltip');
-                if ($tip) {
-                    $tip.tooltip('hide');
-                }
+                $field.data('bootstrapValidator.error').html(message).show();
             },
 
             removeError: function($field) {
+                this.invalidFields[$field.attr('name')] = false;
                 $field.parents('.form-group').removeClass('has-error').addClass('has-success');
-                var $tip = $field.data('bootstrapValidator.tooltip');
-                if ($tip) {
-                    $tip.find('i').attr('class', this.options.iconClass.valid).end()
-                        .tooltip('destroy')
-                        .remove();
-                    $field.removeData('bootstrapValidator.tooltip');
-                }
+                $field.data('bootstrapValidator.error').hide();
             },
 
             startRequest: function($field, validatorName, xhr) {
@@ -192,10 +197,23 @@
                     return;
                 }
 
-                var xhr = this.xhrRequests[field][validatorName];
                 this.numPendingRequests--;
+                var xhr = this.xhrRequests[field][validatorName];
                 xhr.abort();
                 delete this.xhrRequests[field][validatorName];
+            },
+
+            isValid: function() {
+                console.log(this.numPendingRequests);
+                if (this.numPendingRequests > 0) {
+                    return false;
+                }
+                for (var field in this.invalidFields) {
+                    if (this.invalidFields[field]) {
+                        return false;
+                    }
+                }
+                return true;
             }
         }
     });