diff options
| author | Igor Minar | 2012-08-07 22:08:03 -0700 | 
|---|---|---|
| committer | Igor Minar | 2012-08-10 14:21:02 -0700 | 
| commit | 663ccc5449dfa6e8d5358d4955df4e9f332a34e1 (patch) | |
| tree | 5508e66dfda5cc2d67c0c5e995e7a01cf392aedc /src/ng/directive/form.js | |
| parent | 263f47819fa0f4510a5318778a1d3b5cb55fe4aa (diff) | |
| download | angular.js-663ccc5449dfa6e8d5358d4955df4e9f332a34e1.tar.bz2 | |
fix(form): prevent page reload when form destroyed
this fix ensures that we prevent the default action on form submission
(full page reload) even in cases when the form is being destroyed as
a result of the submit event handler (e.g. when route change is
triggered).
The fix is more complicated than I'd like it to be mainly because
we need to ensure that we don't create circular references between
js closures and dom elements via DOM event handlers that would then
result in a memory leak.
Also the differences between IE8, IE9 and normal browsers make testing
this ugly.
Closes #1238
Diffstat (limited to 'src/ng/directive/form.js')
| -rw-r--r-- | src/ng/directive/form.js | 78 | 
1 files changed, 51 insertions, 27 deletions
| diff --git a/src/ng/directive/form.js b/src/ng/directive/form.js index cc229759..6b876e01 100644 --- a/src/ng/directive/form.js +++ b/src/ng/directive/form.js @@ -227,38 +227,62 @@ function FormController(element, attrs) {        </doc:scenario>      </doc:example>   */ -var formDirectiveDir = { -  name: 'form', -  restrict: 'E', -  controller: FormController, -  compile: function() { -    return { -      pre: function(scope, formElement, attr, controller) { -        if (!attr.action) { -          formElement.bind('submit', function(event) { -            event.preventDefault(); -          }); -        } +var formDirectiveFactory = function(isNgForm) { +  return ['$timeout', function($timeout) { +    var formDirective = { +      name: 'form', +      restrict: 'E', +      controller: FormController, +      compile: function() { +        return { +          pre: function(scope, formElement, attr, controller) { +            if (!attr.action) { +              // we can't use jq events because if a form is destroyed during submission the default +              // action is not prevented. see #1238 +              // +              // IE 9 is not affected because it doesn't fire a submit event and try to do a full +              // page reload if the form was destroyed by submission of the form via a click handler +              // on a button in the form. Looks like an IE9 specific bug. +              var preventDefaultListener = function(event) { +                event.preventDefault +                  ? event.preventDefault() +                  : event.returnValue = false; // IE +              }; -        var parentFormCtrl = formElement.parent().controller('form'), -            alias = attr.name || attr.ngForm; +              addEventListenerFn(formElement[0], 'submit', preventDefaultListener); + +              // unregister the preventDefault listener so that we don't not leak memory but in a +              // way that will achieve the prevention of the default action. +              formElement.bind('$destroy', function() { +                $timeout(function() { +                  removeEventListenerFn(formElement[0], 'submit', preventDefaultListener); +                }, 0, false); +              }); +            } + +            var parentFormCtrl = formElement.parent().controller('form'), +                alias = attr.name || attr.ngForm; -        if (alias) { -          scope[alias] = controller; -        } -        if (parentFormCtrl) { -          formElement.bind('$destroy', function() { -            parentFormCtrl.$removeControl(controller);              if (alias) { -              scope[alias] = undefined; +              scope[alias] = controller;              } -            extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards -          }); -        } +            if (parentFormCtrl) { +              formElement.bind('$destroy', function() { +                parentFormCtrl.$removeControl(controller); +                if (alias) { +                  scope[alias] = undefined; +                } +                extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards +              }); +            } +          } +        };        }      }; -  } + +    return isNgForm ? extend(copy(formDirective), {restrict: 'EAC'}) : formDirective; +  }];  }; -var formDirective = valueFn(formDirectiveDir); -var ngFormDirective = valueFn(extend(copy(formDirectiveDir), {restrict: 'EAC'})); +var formDirective = formDirectiveFactory(); +var ngFormDirective = formDirectiveFactory(true); | 
