phuoc 11 years ago
parent
commit
f5d3a96893
4 changed files with 228 additions and 194 deletions
  1. 3 3
      demo/dynamic.html
  2. 112 95
      dist/js/bootstrapValidator.js
  3. 2 2
      dist/js/bootstrapValidator.min.js
  4. 111 94
      src/js/bootstrapValidator.js

+ 3 - 3
demo/dynamic.html

@@ -39,7 +39,7 @@
                     </div>
                     <div class="form-group hide" id="template">
                         <div class="col-lg-offset-3 col-lg-5">
-                            <input class="form-control" type="text" name="answers[]" />
+                            <input class="form-control" type="text" />
                         </div>
                         <div class="col-lg-2">
                             <button type="button" class="btn btn-default btn-sm removeButton">Remove</button>
@@ -64,12 +64,12 @@
         $('.addButton').on('click', function() {
             var $row = $('#template').clone().removeAttr('id').insertBefore($('#template')).removeClass('hide');
 
-            var $answer = $row.find('[name="answers[]"]');
+            var $answer = $row.find('input').eq(0).attr('name', 'answers[]');
             $('#defaultForm').bootstrapValidator('addFieldElement', $answer);
 
             $row.on('click', '.removeButton', function(e) {
-                $row.remove();
                 $('#defaultForm').bootstrapValidator('removeFieldElement', $answer);
+                $row.remove();
             });
         });
 

+ 112 - 95
dist/js/bootstrapValidator.js

@@ -33,6 +33,8 @@
         // The flag to indicate that the form is ready to submit when a remote/callback validator returns
         this._submitIfValid = null;
 
+        this._cacheFields = {};
+
         this._init();
     };
 
@@ -218,8 +220,6 @@
             for (var field in this.options.fields) {
                 this._initField(field);
             }
-
-            this.setLiveMode(this.options.live);
         },
 
         /**
@@ -235,7 +235,7 @@
             var fields = this.getFieldElements(field);
 
             // We don't need to validate non-existing fields
-            if (fields == null) {
+            if (fields == []) {
                 delete this.options.fields[field];
                 return;
             }
@@ -244,65 +244,91 @@
                     delete this.options.fields[field].validators[validatorName];
                 }
             }
+            if (this.options.fields[field]['enabled'] == null) {
+                this.options.fields[field]['enabled'] = true;
+            }
 
-            var that      = this,
-                type      = fields.attr('type'),
-                event     = ('radio' == type || 'checkbox' == type || 'file' == type || 'SELECT' == fields[0].tagName) ? 'change' : that._changeEvent,
-                total     = fields.length,
-                updateAll = (total == 1) || ('radio' == type) || ('checkbox' == type);
-
-            for (var i = 0; i < total; i++) {
-                var $field   = $(fields[i]),
-                    $parent  = $field.parents('.form-group'),
-                    // Allow user to indicate where the error messages are shown
-                    $message = this.options.fields[field].container ? $parent.find(this.options.fields[field].container) : this._getMessageContainer($field);
-
-                // Set the attribute to indicate the fields which are defined by selector
-                if (!$field.attr('data-bv-field')) {
-                    $field.attr('data-bv-field', field);
-                }
+            for (var i = 0; i < fields.length; i++) {
+                this._initFieldElement($(fields[i]));
+            }
+        },
 
-                // Whenever the user change the field value, mark it as not validated yet
-                $field.on(event + '.update.bv', function() {
-                    // Reset the flag
-                    that._submitIfValid = false;
-                    updateAll ? that.updateStatus(field, that.STATUS_NOT_VALIDATED)
-                              : that.updateElementStatus($(this), that.STATUS_NOT_VALIDATED);
-                });
+        _initFieldElement: function($field) {
+            var that     = this,
+                field    = $field.attr('name') || $field.attr('data-bv-field'),
+                fields   = this.getFieldElements(field),
+                index    = fields.index($field),
+                type     = $field.attr('type'),
+                total     = fields.length,
+                updateAll = (total == 1) || ('radio' == type) || ('checkbox' == type),
+                $parent  = $field.parents('.form-group'),
+                // Allow user to indicate where the error messages are shown
+                $message = this.options.fields[field].container ? $parent.find(this.options.fields[field].container) : this._getMessageContainer($field);
+
+            // Remove all error messages and feedback icons
+            //$message.find('.help-block[data-bv-validator]').remove();
+            //$parent.find('i[data-bv-field]').remove();
+
+            // Set the attribute to indicate the fields which are defined by selector
+            if (!$field.attr('data-bv-field')) {
+                $field.attr('data-bv-field', field);
+            }
+
+            // Whenever the user change the field value, mark it as not validated yet
+            var event = ('radio' == type || 'checkbox' == type || 'file' == type || 'SELECT' == $field.get(0).tagName) ? 'change' : this._changeEvent;
+            $field.on(event + '.update.bv', function() {
+                // Reset the flag
+                that._submitIfValid = false;
+                that.updateElementStatus($(this), that.STATUS_NOT_VALIDATED);
+            });
 
-                // Create help block elements for showing the error messages
-                $field.data('bv.messages', $message);
-                for (validatorName in this.options.fields[field].validators) {
-                    $field.data('bv.result.' + validatorName, this.STATUS_NOT_VALIDATED);
-
-                    if (!updateAll || i == total - 1) {
-                        $('<small/>')
-                            .css('display', 'none')
-                            .attr('data-bv-validator', validatorName)
-                            .html(this.options.fields[field].validators[validatorName].message || this.options.fields[field].message || this.options.message)
-                            .addClass('help-block')
-                            .appendTo($message);
-                    }
+            // Create help block elements for showing the error messages
+            $field.data('bv.messages', $message);
+            for (var validatorName in this.options.fields[field].validators) {
+                $field.data('bv.result.' + validatorName, this.STATUS_NOT_VALIDATED);
+
+                if (!updateAll || index == total - 1) {
+                    $('<small/>')
+                        .css('display', 'none')
+                        .attr('data-bv-validator', validatorName)
+                        .html(this.options.fields[field].validators[validatorName].message || this.options.fields[field].message || this.options.message)
+                        .addClass('help-block')
+                        .appendTo($message);
                 }
+            }
 
-                // Prepare the feedback icons
-                // Available from Bootstrap 3.1 (http://getbootstrap.com/css/#forms-control-validation)
-                if (this.options.feedbackIcons
-                    && this.options.feedbackIcons.validating && this.options.feedbackIcons.invalid && this.options.feedbackIcons.valid
-                    && (!updateAll || i == total - 1))
-                {
-                    $parent.addClass('has-feedback');
-                    var $icon = $('<i/>').css('display', 'none').addClass('form-control-feedback').attr('data-bv-field', field).insertAfter($field);
-                    // The feedback icon does not render correctly if there is no label
-                    // https://github.com/twbs/bootstrap/issues/12873
-                    if ($parent.find('label').length == 0) {
-                        $icon.css('top', 0);
-                    }
+            // Prepare the feedback icons
+            // Available from Bootstrap 3.1 (http://getbootstrap.com/css/#forms-control-validation)
+            if (this.options.feedbackIcons
+                && this.options.feedbackIcons.validating && this.options.feedbackIcons.invalid && this.options.feedbackIcons.valid
+                && (!updateAll || index == total - 1))
+            {
+                $parent.addClass('has-feedback');
+                var $icon = $('<i/>').css('display', 'none').addClass('form-control-feedback').attr('data-bv-field', field).insertAfter($field);
+                // The feedback icon does not render correctly if there is no label
+                // https://github.com/twbs/bootstrap/issues/12873
+                if ($parent.find('label').length == 0) {
+                    $icon.css('top', 0);
                 }
             }
 
-            if (this.options.fields[field]['enabled'] == null) {
-                this.options.fields[field]['enabled'] = true;
+            // Set live mode
+            var trigger = this.options.fields[field].trigger || this.options.trigger || event,
+                events  = $.map(trigger.split(' '), function(item) {
+                    return item + '.live.bv';
+                }).join(' ');
+            switch (this.options.live) {
+                case 'submitted':
+                    break;
+                case 'disabled':
+                    $field.off(events);
+                    break;
+                case 'enabled':
+                default:
+                    $field.off(events).on(events, function() {
+                        that.validateFieldElement($(this));
+                    });
+                    break;
             }
         },
 
@@ -340,7 +366,28 @@
         _submit: function() {
             if (!this.isValid()) {
                 if ('submitted' == this.options.live) {
-                    this.setLiveMode('enabled');
+                    // Enable live mode
+                    this.options.live = 'enabled';
+                    var that = this;
+                    for (var field in this.options.fields) {
+                        (function(f) {
+                            var fields  = that.getFieldElements(f);
+                            if (fields.length) {
+                                var type    = $(fields[0]).attr('type'),
+                                    event   = ('radio' == type || 'checkbox' == type || 'file' == type || 'SELECT' == $(fields[0]).get(0).tagName) ? 'change' : that._changeEvent,
+                                    trigger = that.options.fields[field].trigger || that.options.trigger || event,
+                                    events  = $.map(trigger.split(' '), function(item) {
+                                        return item + '.live.bv';
+                                    }).join(' ');
+
+                                for (var i = 0; i < fields.length; i++) {
+                                    $(fields[i]).off(events).on(events, function() {
+                                        that.validateFieldElement($(this));
+                                    });
+                                }
+                            }
+                        })(field);
+                    }
                 }
 
                 // Focus to the first invalid field
@@ -397,48 +444,13 @@
          * @returns {null|jQuery[]}
          */
         getFieldElements: function(field) {
-            var fields = this.options.fields[field].selector ? $(this.options.fields[field].selector) : this.$form.find('[name="' + field + '"]');
-            return (fields.length == 0) ? null : fields;
-        },
-
-        /**
-         * Set live validating mode
-         *
-         * @param {String} mode Live validating mode. Can be 'enabled', 'disabled', 'submitted'
-         * @returns {BootstrapValidator}
-         */
-        setLiveMode: function(mode) {
-            this.options.live = mode;
-            if ('submitted' == mode) {
-                return this;
+            if (!this._cacheFields[field]) {
+                this._cacheFields[field] = this.options.fields[field].selector
+                                         ? $(this.options.fields[field].selector)
+                                         : this.$form.find('[name="' + field + '"]');
             }
 
-            var that = this;
-            for (var field in this.options.fields) {
-                (function(f) {
-                    var fields = that.getFieldElements(f);
-                    if (fields) {
-                        var type      = fields.attr('type'),
-                            total     = fields.length,
-                            trigger   = that.options.fields[field].trigger
-                                        || that.options.trigger
-                                        || (('radio' == type || 'checkbox' == type || 'file' == type || 'SELECT' == fields[0].tagName) ? 'change' : that._changeEvent),
-                            events    = $.map(trigger.split(' '), function(item) {
-                                return item + '.live.bv';
-                            }).join(' ');
-
-                        for (var i = 0; i < total; i++) {
-                            ('enabled' == mode)
-                                ? $(fields[i]).on(events, function() {
-                                    that.validateFieldElement($(this));
-                                })
-                                : $(fields[i]).off(events);
-                        }
-                    }
-                })(field);
-            }
-
-            return this;
+            return this._cacheFields[field];
         },
 
         /**
@@ -717,10 +729,15 @@
         // Useful APIs which aren't used internally
 
         addFieldElement: function($field, options) {
+            var field = $field.attr('name') || $field.attr('data-bv-field');
+            this._cacheFields[field].push($field);
+            this._initFieldElement($field);
             return this;
         },
 
         removeFieldElement: function($field) {
+            var field = $field.attr('name') || $field.attr('data-bv-field');
+            delete this._cacheFields[field];
             return this;
         },
 

File diff suppressed because it is too large
+ 2 - 2
dist/js/bootstrapValidator.min.js


+ 111 - 94
src/js/bootstrapValidator.js

@@ -32,6 +32,8 @@
         // The flag to indicate that the form is ready to submit when a remote/callback validator returns
         this._submitIfValid = null;
 
+        this._cacheFields = {};
+
         this._init();
     };
 
@@ -217,8 +219,6 @@
             for (var field in this.options.fields) {
                 this._initField(field);
             }
-
-            this.setLiveMode(this.options.live);
         },
 
         /**
@@ -234,7 +234,7 @@
             var fields = this.getFieldElements(field);
 
             // We don't need to validate non-existing fields
-            if (fields == null) {
+            if (fields == []) {
                 delete this.options.fields[field];
                 return;
             }
@@ -243,65 +243,91 @@
                     delete this.options.fields[field].validators[validatorName];
                 }
             }
+            if (this.options.fields[field]['enabled'] == null) {
+                this.options.fields[field]['enabled'] = true;
+            }
 
-            var that      = this,
-                type      = fields.attr('type'),
-                event     = ('radio' == type || 'checkbox' == type || 'file' == type || 'SELECT' == fields[0].tagName) ? 'change' : that._changeEvent,
-                total     = fields.length,
-                updateAll = (total == 1) || ('radio' == type) || ('checkbox' == type);
-
-            for (var i = 0; i < total; i++) {
-                var $field   = $(fields[i]),
-                    $parent  = $field.parents('.form-group'),
-                    // Allow user to indicate where the error messages are shown
-                    $message = this.options.fields[field].container ? $parent.find(this.options.fields[field].container) : this._getMessageContainer($field);
+            for (var i = 0; i < fields.length; i++) {
+                this._initFieldElement($(fields[i]));
+            }
+        },
 
-                // Set the attribute to indicate the fields which are defined by selector
-                if (!$field.attr('data-bv-field')) {
-                    $field.attr('data-bv-field', field);
-                }
+        _initFieldElement: function($field) {
+            var that     = this,
+                field    = $field.attr('name') || $field.attr('data-bv-field'),
+                fields   = this.getFieldElements(field),
+                index    = fields.index($field),
+                type     = $field.attr('type'),
+                total     = fields.length,
+                updateAll = (total == 1) || ('radio' == type) || ('checkbox' == type),
+                $parent  = $field.parents('.form-group'),
+                // Allow user to indicate where the error messages are shown
+                $message = this.options.fields[field].container ? $parent.find(this.options.fields[field].container) : this._getMessageContainer($field);
+
+            // Remove all error messages and feedback icons
+            //$message.find('.help-block[data-bv-validator]').remove();
+            //$parent.find('i[data-bv-field]').remove();
+
+            // Set the attribute to indicate the fields which are defined by selector
+            if (!$field.attr('data-bv-field')) {
+                $field.attr('data-bv-field', field);
+            }
 
-                // Whenever the user change the field value, mark it as not validated yet
-                $field.on(event + '.update.bv', function() {
-                    // Reset the flag
-                    that._submitIfValid = false;
-                    updateAll ? that.updateStatus(field, that.STATUS_NOT_VALIDATED)
-                              : that.updateElementStatus($(this), that.STATUS_NOT_VALIDATED);
-                });
+            // Whenever the user change the field value, mark it as not validated yet
+            var event = ('radio' == type || 'checkbox' == type || 'file' == type || 'SELECT' == $field.get(0).tagName) ? 'change' : this._changeEvent;
+            $field.on(event + '.update.bv', function() {
+                // Reset the flag
+                that._submitIfValid = false;
+                that.updateElementStatus($(this), that.STATUS_NOT_VALIDATED);
+            });
 
-                // Create help block elements for showing the error messages
-                $field.data('bv.messages', $message);
-                for (validatorName in this.options.fields[field].validators) {
-                    $field.data('bv.result.' + validatorName, this.STATUS_NOT_VALIDATED);
-
-                    if (!updateAll || i == total - 1) {
-                        $('<small/>')
-                            .css('display', 'none')
-                            .attr('data-bv-validator', validatorName)
-                            .html(this.options.fields[field].validators[validatorName].message || this.options.fields[field].message || this.options.message)
-                            .addClass('help-block')
-                            .appendTo($message);
-                    }
+            // Create help block elements for showing the error messages
+            $field.data('bv.messages', $message);
+            for (var validatorName in this.options.fields[field].validators) {
+                $field.data('bv.result.' + validatorName, this.STATUS_NOT_VALIDATED);
+
+                if (!updateAll || index == total - 1) {
+                    $('<small/>')
+                        .css('display', 'none')
+                        .attr('data-bv-validator', validatorName)
+                        .html(this.options.fields[field].validators[validatorName].message || this.options.fields[field].message || this.options.message)
+                        .addClass('help-block')
+                        .appendTo($message);
                 }
+            }
 
-                // Prepare the feedback icons
-                // Available from Bootstrap 3.1 (http://getbootstrap.com/css/#forms-control-validation)
-                if (this.options.feedbackIcons
-                    && this.options.feedbackIcons.validating && this.options.feedbackIcons.invalid && this.options.feedbackIcons.valid
-                    && (!updateAll || i == total - 1))
-                {
-                    $parent.addClass('has-feedback');
-                    var $icon = $('<i/>').css('display', 'none').addClass('form-control-feedback').attr('data-bv-field', field).insertAfter($field);
-                    // The feedback icon does not render correctly if there is no label
-                    // https://github.com/twbs/bootstrap/issues/12873
-                    if ($parent.find('label').length == 0) {
-                        $icon.css('top', 0);
-                    }
+            // Prepare the feedback icons
+            // Available from Bootstrap 3.1 (http://getbootstrap.com/css/#forms-control-validation)
+            if (this.options.feedbackIcons
+                && this.options.feedbackIcons.validating && this.options.feedbackIcons.invalid && this.options.feedbackIcons.valid
+                && (!updateAll || index == total - 1))
+            {
+                $parent.addClass('has-feedback');
+                var $icon = $('<i/>').css('display', 'none').addClass('form-control-feedback').attr('data-bv-field', field).insertAfter($field);
+                // The feedback icon does not render correctly if there is no label
+                // https://github.com/twbs/bootstrap/issues/12873
+                if ($parent.find('label').length == 0) {
+                    $icon.css('top', 0);
                 }
             }
 
-            if (this.options.fields[field]['enabled'] == null) {
-                this.options.fields[field]['enabled'] = true;
+            // Set live mode
+            var trigger = this.options.fields[field].trigger || this.options.trigger || event,
+                events  = $.map(trigger.split(' '), function(item) {
+                    return item + '.live.bv';
+                }).join(' ');
+            switch (this.options.live) {
+                case 'submitted':
+                    break;
+                case 'disabled':
+                    $field.off(events);
+                    break;
+                case 'enabled':
+                default:
+                    $field.off(events).on(events, function() {
+                        that.validateFieldElement($(this));
+                    });
+                    break;
             }
         },
 
@@ -339,7 +365,28 @@
         _submit: function() {
             if (!this.isValid()) {
                 if ('submitted' == this.options.live) {
-                    this.setLiveMode('enabled');
+                    // Enable live mode
+                    this.options.live = 'enabled';
+                    var that = this;
+                    for (var field in this.options.fields) {
+                        (function(f) {
+                            var fields  = that.getFieldElements(f);
+                            if (fields.length) {
+                                var type    = $(fields[0]).attr('type'),
+                                    event   = ('radio' == type || 'checkbox' == type || 'file' == type || 'SELECT' == $(fields[0]).get(0).tagName) ? 'change' : that._changeEvent,
+                                    trigger = that.options.fields[field].trigger || that.options.trigger || event,
+                                    events  = $.map(trigger.split(' '), function(item) {
+                                        return item + '.live.bv';
+                                    }).join(' ');
+
+                                for (var i = 0; i < fields.length; i++) {
+                                    $(fields[i]).off(events).on(events, function() {
+                                        that.validateFieldElement($(this));
+                                    });
+                                }
+                            }
+                        })(field);
+                    }
                 }
 
                 // Focus to the first invalid field
@@ -396,48 +443,13 @@
          * @returns {null|jQuery[]}
          */
         getFieldElements: function(field) {
-            var fields = this.options.fields[field].selector ? $(this.options.fields[field].selector) : this.$form.find('[name="' + field + '"]');
-            return (fields.length == 0) ? null : fields;
-        },
-
-        /**
-         * Set live validating mode
-         *
-         * @param {String} mode Live validating mode. Can be 'enabled', 'disabled', 'submitted'
-         * @returns {BootstrapValidator}
-         */
-        setLiveMode: function(mode) {
-            this.options.live = mode;
-            if ('submitted' == mode) {
-                return this;
-            }
-
-            var that = this;
-            for (var field in this.options.fields) {
-                (function(f) {
-                    var fields = that.getFieldElements(f);
-                    if (fields) {
-                        var type      = fields.attr('type'),
-                            total     = fields.length,
-                            trigger   = that.options.fields[field].trigger
-                                        || that.options.trigger
-                                        || (('radio' == type || 'checkbox' == type || 'file' == type || 'SELECT' == fields[0].tagName) ? 'change' : that._changeEvent),
-                            events    = $.map(trigger.split(' '), function(item) {
-                                return item + '.live.bv';
-                            }).join(' ');
-
-                        for (var i = 0; i < total; i++) {
-                            ('enabled' == mode)
-                                ? $(fields[i]).on(events, function() {
-                                    that.validateFieldElement($(this));
-                                })
-                                : $(fields[i]).off(events);
-                        }
-                    }
-                })(field);
+            if (!this._cacheFields[field]) {
+                this._cacheFields[field] = this.options.fields[field].selector
+                                         ? $(this.options.fields[field].selector)
+                                         : this.$form.find('[name="' + field + '"]');
             }
 
-            return this;
+            return this._cacheFields[field];
         },
 
         /**
@@ -716,10 +728,15 @@
         // Useful APIs which aren't used internally
 
         addFieldElement: function($field, options) {
+            var field = $field.attr('name') || $field.attr('data-bv-field');
+            this._cacheFields[field].push($field);
+            this._initFieldElement($field);
             return this;
         },
 
         removeFieldElement: function($field) {
+            var field = $field.attr('name') || $field.attr('data-bv-field');
+            delete this._cacheFields[field];
             return this;
         },