ソースを参照

#343, #481, #1045: Fix double submit with defered validators, thanks to @jazzzz

Phuoc Nguyen 11 年 前
コミット
9dedfd1cc1

+ 1 - 0
CHANGELOG.md

@@ -22,6 +22,7 @@ __Improvements__
 * [#1017](https://github.com/nghuuphuoc/bootstrapvalidator/pull/1017): Enable validator when setting ```data-bv-validatorname="data-bv-validatorname"```, thanks to [@jazzzz](https://github.com/jazzzz)
 
 __Bug Fixes__
+* [#343](https://github.com/nghuuphuoc/bootstrapvalidator/issues/343), [#481](https://github.com/nghuuphuoc/bootstrapvalidator/issues/481), [#1045](https://github.com/nghuuphuoc/bootstrapvalidator/pull/1045): Fix double submit with defered validators, thanks to [@jazzzz](https://github.com/jazzzz)
 * [#933](https://github.com/nghuuphuoc/bootstrapvalidator/issues/933), [#959](https://github.com/nghuuphuoc/bootstrapvalidator/issues/959), [#1047](https://github.com/nghuuphuoc/bootstrapvalidator/issues/1047): Tooltip/popover isn't destroyed when the field is valid
 * [#991](https://github.com/nghuuphuoc/bootstrapvalidator/issues/991): The field is validated only one time when setting ```trigger: 'blur'```, ```container: 'tooltip'```
 * [#1014](https://github.com/nghuuphuoc/bootstrapvalidator/pull/1014): Fix [isValidField()](http://bootstrapvalidator.com/api/#is-valid-field) and [validateField()](http://bootstrapvalidator.com/api/#validate-field) methods for fields without validators, thanks to [@jazzzz](https://github.com/jazzzz)

+ 1 - 1
dist/css/bootstrapValidator.min.css

@@ -2,7 +2,7 @@
  * BootstrapValidator (http://bootstrapvalidator.com)
  * The best jQuery plugin to validate form fields. Designed to use with Bootstrap 3
  *
- * @version     v0.5.3-dev, built on 2014-11-03 12:34:21 AM
+ * @version     v0.5.3-dev, built on 2014-11-03 10:35:05 AM
  * @author      https://twitter.com/nghuuphuoc
  * @copyright   (c) 2013 - 2014 Nguyen Huu Phuoc
  * @license     MIT

+ 5 - 4
dist/js/bootstrapValidator.js

@@ -2,7 +2,7 @@
  * BootstrapValidator (http://bootstrapvalidator.com)
  * The best jQuery plugin to validate form fields. Designed to use with Bootstrap 3
  *
- * @version     v0.5.3-dev, built on 2014-11-03 12:34:21 AM
+ * @version     v0.5.3-dev, built on 2014-11-03 10:35:05 AM
  * @author      https://twitter.com/nghuuphuoc
  * @copyright   (c) 2013 - 2014 Nguyen Huu Phuoc
  * @license     MIT
@@ -749,8 +749,7 @@ if (typeof jQuery === 'undefined') {
          * @param {String} field The field name
          * @returns {Boolean}
          */
-        _isVerboseField: function(field)
-        {
+        _isVerboseField: function(field) {
             if (this.options.fields[field].verbose === 'true' || this.options.fields[field].verbose === true) {
                 return true;
             }
@@ -810,7 +809,6 @@ if (typeof jQuery === 'undefined') {
             return option ? options.validators[validator][option] : options.validators[validator];
         },
 
-
         /**
          * Disable/enable submit buttons
          *
@@ -839,11 +837,13 @@ if (typeof jQuery === 'undefined') {
             }
             this.disableSubmitButtons(true);
 
+            this._submitIfValid = false;
             for (var field in this.options.fields) {
                 this.validateField(field);
             }
 
             this._submit();
+            this._submitIfValid = true;
 
             return this;
         },
@@ -1005,6 +1005,7 @@ if (typeof jQuery === 'undefined') {
 
             if (status === this.STATUS_NOT_VALIDATED) {
                 // Reset the flag
+                // To prevent the form from doing submit when a deferred validator returns true while typing
                 this._submitIfValid = false;
             }
 

ファイルの差分が大きいため隠しています
+ 2 - 2
dist/js/bootstrapValidator.min.js


+ 2 - 3
src/js/bootstrapValidator.js

@@ -749,8 +749,7 @@ if (typeof jQuery === 'undefined') {
          * @param {String} field The field name
          * @returns {Boolean}
          */
-        _isVerboseField: function(field)
-        {
+        _isVerboseField: function(field) {
             if (this.options.fields[field].verbose === 'true' || this.options.fields[field].verbose === true) {
                 return true;
             }
@@ -810,7 +809,6 @@ if (typeof jQuery === 'undefined') {
             return option ? options.validators[validator][option] : options.validators[validator];
         },
 
-
         /**
          * Disable/enable submit buttons
          *
@@ -1007,6 +1005,7 @@ if (typeof jQuery === 'undefined') {
 
             if (status === this.STATUS_NOT_VALIDATED) {
                 // Reset the flag
+                // To prevent the form from doing submit when a deferred validator returns true while typing
                 this._submitIfValid = false;
             }
 

+ 167 - 6
test/spec.js

@@ -539,6 +539,8 @@ describe('enable validators', function() {
     });
 });
 
+var defaultOptions = $.fn.bootstrapValidator.DEFAULT_OPTIONS;
+
 TestSuite = $.extend({}, TestSuite, {
     Event: {
         onEmailValid: function(e, data) {
@@ -1024,8 +1026,6 @@ describe('event field trigger with default events', function() {
 });
 
 describe('event form trigger with events changed', function() {
-    var defaultOptions = $.fn.bootstrapValidator.DEFAULT_OPTIONS;
-
     beforeEach(function() {
         $.fn.bootstrapValidator.DEFAULT_OPTIONS = $.extend({}, $.fn.bootstrapValidator.DEFAULT_OPTIONS, {
             events: {
@@ -1072,8 +1072,8 @@ describe('event form trigger with events changed', function() {
     });
 
     afterEach(function() {
-        $.fn.bootstrapValidator.DEFAULT_OPTIONS = defaultOptions;
         $('#eventForm2').bootstrapValidator('destroy').remove();
+        $.fn.bootstrapValidator.DEFAULT_OPTIONS = defaultOptions;
     });
 
     it('triggers bv.form.success', function() {
@@ -1100,8 +1100,6 @@ describe('event form trigger with events changed', function() {
 });
 
 describe('event field trigger with events changed', function() {
-    var defaultOptions = $.fn.bootstrapValidator.DEFAULT_OPTIONS;
-
     beforeEach(function() {
         $.fn.bootstrapValidator.DEFAULT_OPTIONS = $.extend({}, $.fn.bootstrapValidator.DEFAULT_OPTIONS, {
             events: {
@@ -1148,8 +1146,8 @@ describe('event field trigger with events changed', function() {
     });
 
     afterEach(function() {
-        $.fn.bootstrapValidator.DEFAULT_OPTIONS = defaultOptions;
         $('#eventForm4').bootstrapValidator('destroy').remove();
+        $.fn.bootstrapValidator.DEFAULT_OPTIONS = defaultOptions;
     });
 
     it('triggers success.field.bv', function() {
@@ -1995,6 +1993,169 @@ describe('message', function() {
     });
 });
 
+describe('submit', function() {
+    var submitted, originalTimeout;
+
+    $.fn.bootstrapValidator.validators.fake_remote = {
+        validate: function(validator, $field, options) {
+            var dfd = new $.Deferred();
+            setTimeout(function() {
+                dfd.resolve($field, 'fake_remote', { valid: options.valid });
+            }, 0);
+            return dfd;
+        }
+    };
+    
+    beforeEach(function() {
+        $([
+            '<form id="submitForm" class="form-horizontal" role="form">',
+                '<div class="form-group">',
+                    '<input name="username" type="text" class="form-control" value="me" required />',
+                '</div>',
+                '<button id="sendButton" type="submit" class="btn btn-default">Send</button>',
+            '</form>'
+        ].join('\n')).appendTo('body');
+
+        this.$form = $('#submitForm');
+        this.$form
+            .bootstrapValidator()
+            .on('success.form.bv', function(e) {
+                e.preventDefault();
+                ++submitted;
+            })
+            .submit(function(e) {
+                e.preventDefault();
+            });
+            
+        submitted      = 0;
+        this.bv        = this.$form.data('bootstrapValidator');
+        this.$username = this.bv.getFieldElements('username');
+
+        originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
+        jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
+    });
+
+    afterEach(function() {
+        $('#submitForm').bootstrapValidator('destroy').remove();
+        jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
+    });
+
+    // #481
+    it('without callback nor remote', function(done) {
+        $('#sendButton').click();
+        setTimeout(function() {
+            expect(submitted).toBe(1);
+            done();
+        }, 0);
+    });
+
+    // #481
+    it('with callback returning true', function(done) {
+        this.bv.addField('username', {
+            validators: {
+                callback: {
+                    message: 'Please enter an username',
+                    callback: function(value, validator, $field) {
+                        return true;
+                    }
+                }
+            }
+        });
+        $('#sendButton').click();
+        setTimeout(function() {
+            expect(submitted).toBe(1);
+            done();
+        }, 0);
+    });
+
+    // #481
+    it('with callback returning false', function(done) {
+        this.bv.addField('username', {
+            validators: {
+                callback: {
+                    message: 'Please enter an username',
+                    callback: function(value, validator, $field) {
+                        return false;
+                    }
+                }
+            }
+        });
+        $('#sendButton').click();
+        setTimeout(function() {
+            expect(submitted).toBe(0);
+            done();
+        }, 0);
+    });
+
+    // #481
+    it('with remote returning true', function(done) {
+        this.bv.addField('username', {
+            validators: {
+                remote: {
+                    url: 'http://echo.jsontest.com/valid/true',
+                    message: 'The username is not available'
+                }
+            }
+        });
+        $('#sendButton').click();
+        setTimeout(function() {
+            expect(submitted).toBe(1);
+            done();
+        }, 3000);
+    });
+
+    // #481
+    it('with remote returning false', function(done) {
+        this.bv.addField('username', {
+            validators: {
+                remote: {
+                    url: 'http://echo.jsontest.com/valid/false',
+                    message: 'The username is not available'
+                }
+            }
+        });
+        $('#sendButton').click();
+        setTimeout(function() {
+            expect(submitted).toBe(0);
+            done();
+        }, 3000);
+    });
+
+    // #481
+    it('with fake remote returning true', function(done) {
+        this.bv.addField('username', {
+            validators: {
+                fake_remote: {
+                    message: 'The username is not available',
+                    valid: true
+                }
+            }
+        });
+        $('#sendButton').click();
+        setTimeout(function() {
+            expect(submitted).toBe(1);
+            done();
+        }, 100);
+    });
+
+    // #481
+    it('with fake remote returning false', function(done) {
+        this.bv.addField('username', {
+            validators: {
+                fake_remote: {
+                    message: 'The username is not available',
+                    valid: false
+                }
+            }
+        });
+        $('#sendButton').click();
+        setTimeout(function() {
+            expect(submitted).toBe(0);
+            done();
+        }, 100);
+    });
+});
+
 describe('verbose option', function() {
     beforeEach(function() {
         $([

+ 3 - 9
test/spec/event.js

@@ -1,4 +1,4 @@
-var originalDefaultOptions = $.fn.bootstrapValidator.DEFAULT_OPTIONS;
+var defaultOptions = $.fn.bootstrapValidator.DEFAULT_OPTIONS;
 
 TestSuite = $.extend({}, TestSuite, {
     Event: {
@@ -485,8 +485,6 @@ describe('event field trigger with default events', function() {
 });
 
 describe('event form trigger with events changed', function() {
-    var defaultOptions = $.fn.bootstrapValidator.DEFAULT_OPTIONS;
-
     beforeEach(function() {
         $.fn.bootstrapValidator.DEFAULT_OPTIONS = $.extend({}, $.fn.bootstrapValidator.DEFAULT_OPTIONS, {
             events: {
@@ -533,9 +531,8 @@ describe('event form trigger with events changed', function() {
     });
 
     afterEach(function() {
-        $.fn.bootstrapValidator.DEFAULT_OPTIONS = defaultOptions;
         $('#eventForm2').bootstrapValidator('destroy').remove();
-        $.fn.bootstrapValidator.DEFAULT_OPTIONS = originalDefaultOptions;
+        $.fn.bootstrapValidator.DEFAULT_OPTIONS = defaultOptions;
     });
 
     it('triggers bv.form.success', function() {
@@ -562,8 +559,6 @@ describe('event form trigger with events changed', function() {
 });
 
 describe('event field trigger with events changed', function() {
-    var defaultOptions = $.fn.bootstrapValidator.DEFAULT_OPTIONS;
-
     beforeEach(function() {
         $.fn.bootstrapValidator.DEFAULT_OPTIONS = $.extend({}, $.fn.bootstrapValidator.DEFAULT_OPTIONS, {
             events: {
@@ -610,9 +605,8 @@ describe('event field trigger with events changed', function() {
     });
 
     afterEach(function() {
-        $.fn.bootstrapValidator.DEFAULT_OPTIONS = defaultOptions;
         $('#eventForm4').bootstrapValidator('destroy').remove();
-        $.fn.bootstrapValidator.DEFAULT_OPTIONS = originalDefaultOptions;
+        $.fn.bootstrapValidator.DEFAULT_OPTIONS = defaultOptions;
     });
 
     it('triggers success.field.bv', function() {

+ 60 - 33
test/spec/submit.js

@@ -1,19 +1,9 @@
 describe('submit', function() {
-
-    var submitted;
-
-    // Override the options
-    $.extend($.fn.bootstrapValidator.DEFAULT_OPTIONS, {
-        feedbackIcons: {
-            valid: 'glyphicon glyphicon-ok',
-            invalid: 'glyphicon glyphicon-remove',
-            validating: 'glyphicon glyphicon-refresh'
-        }
-    });
+    var submitted, originalTimeout;
 
     $.fn.bootstrapValidator.validators.fake_remote = {
         validate: function(validator, $field, options) {
-            var dfd   = new $.Deferred();
+            var dfd = new $.Deferred();
             setTimeout(function() {
                 dfd.resolve($field, 'fake_remote', { valid: options.valid });
             }, 0);
@@ -31,23 +21,28 @@ describe('submit', function() {
             '</form>'
         ].join('\n')).appendTo('body');
 
-        this.$form     = $('#submitForm');
-        this.$form.bootstrapValidator()
+        this.$form = $('#submitForm');
+        this.$form
+            .bootstrapValidator()
             .on('success.form.bv', function(e) {
                 e.preventDefault();
                 ++submitted;
+            })
+            .submit(function(e) {
+                e.preventDefault();
             });
-        this.$form.submit(function(e) {
-            e.preventDefault();
-        });
             
         submitted      = 0;
         this.bv        = this.$form.data('bootstrapValidator');
         this.$username = this.bv.getFieldElements('username');
+
+        originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
+        jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
     });
 
     afterEach(function() {
         $('#submitForm').bootstrapValidator('destroy').remove();
+        jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
     });
 
     // #481
@@ -58,14 +53,14 @@ describe('submit', function() {
             done();
         }, 0);
     });
-    
+
     // #481
     it('with callback returning true', function(done) {
         this.bv.addField('username', {
             validators: {
                 callback: {
                     message: 'Please enter an username',
-                    callback: function (value, validator, $field) {
+                    callback: function(value, validator, $field) {
                         return true;
                     }
                 }
@@ -79,32 +74,48 @@ describe('submit', function() {
     });
 
     // #481
-    it('with fake remote returning true', function(done) {
+    it('with callback returning false', function(done) {
         this.bv.addField('username', {
             validators: {
-                fake_remote: {
+                callback: {
                     message: 'Please enter an username',
-                    valid: true
+                    callback: function(value, validator, $field) {
+                        return false;
+                    }
                 }
             }
         });
         $('#sendButton').click();
         setTimeout(function() {
-            expect(submitted).toBe(1);
+            expect(submitted).toBe(0);
             done();
-        }, 100);
+        }, 0);
     });
 
+    // #481
+    it('with remote returning true', function(done) {
+        this.bv.addField('username', {
+            validators: {
+                remote: {
+                    url: 'http://echo.jsontest.com/valid/true',
+                    message: 'The username is not available'
+                }
+            }
+        });
+        $('#sendButton').click();
+        setTimeout(function() {
+            expect(submitted).toBe(1);
+            done();
+        }, 3000);
+    });
 
     // #481
-    it('with callback returning false', function(done) {
+    it('with remote returning false', function(done) {
         this.bv.addField('username', {
             validators: {
-                callback: {
-                    message: 'Please enter an username',
-                    callback: function (value, validator, $field) {
-                        return false;
-                    }
+                remote: {
+                    url: 'http://echo.jsontest.com/valid/false',
+                    message: 'The username is not available'
                 }
             }
         });
@@ -112,7 +123,24 @@ describe('submit', function() {
         setTimeout(function() {
             expect(submitted).toBe(0);
             done();
-        }, 0);
+        }, 3000);
+    });
+
+    // #481
+    it('with fake remote returning true', function(done) {
+        this.bv.addField('username', {
+            validators: {
+                fake_remote: {
+                    message: 'The username is not available',
+                    valid: true
+                }
+            }
+        });
+        $('#sendButton').click();
+        setTimeout(function() {
+            expect(submitted).toBe(1);
+            done();
+        }, 100);
     });
 
     // #481
@@ -120,7 +148,7 @@ describe('submit', function() {
         this.bv.addField('username', {
             validators: {
                 fake_remote: {
-                    message: 'Please enter an username',
+                    message: 'The username is not available',
                     valid: false
                 }
             }
@@ -131,5 +159,4 @@ describe('submit', function() {
             done();
         }, 100);
     });
-
 });