diff --git a/src/ng/directive/form.js b/src/ng/directive/form.js index 23fa8d8b91e8..0369eb4eb2e8 100644 --- a/src/ng/directive/form.js +++ b/src/ng/directive/form.js @@ -74,6 +74,23 @@ function FormController(element, attrs, $scope, $animate) { $animate.addClass(element, (isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey); } + /** + * @ngdoc method + * @name form.FormController#$commitViewValue + * + * @description + * Commit all form controls pending updates to the `$modelValue`. + * + * Updates may be pending by a debounced event or because the input is waiting for a some future + * event defined in `ng-model-options`. This method is rarely needed as `NgModelController` + * usually handles calling this in response to input events. + */ + form.$commitViewValue = function() { + forEach(controls, function(control) { + control.$commitViewValue(); + }); + }; + /** * @ngdoc method * @name form.FormController#$addControl @@ -286,6 +303,10 @@ function FormController(element, attrs, $scope, $animate) { * hitting enter in any of the input fields will trigger the click handler on the *first* button or * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`) * + * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is + * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit` + * to have access to the updated model. + * * @param {string=} name Name of the form. If specified, the form controller will be published into * related scope, under this name. * @@ -381,19 +402,23 @@ var formDirectiveFactory = function(isNgForm) { // 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) { + var handleFormSubmission = function(event) { + scope.$apply(function() { + controller.$commitViewValue(); + }); + event.preventDefault ? event.preventDefault() : event.returnValue = false; // IE }; - addEventListenerFn(formElement[0], 'submit', preventDefaultListener); + addEventListenerFn(formElement[0], 'submit', handleFormSubmission); // 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.on('$destroy', function() { $timeout(function() { - removeEventListenerFn(formElement[0], 'submit', preventDefaultListener); + removeEventListenerFn(formElement[0], 'submit', handleFormSubmission); }, 0, false); }); } diff --git a/test/ng/directive/formSpec.js b/test/ng/directive/formSpec.js index b9f0ded777b9..03829da12d07 100644 --- a/test/ng/directive/formSpec.js +++ b/test/ng/directive/formSpec.js @@ -154,6 +154,57 @@ describe('form', function() { }).toThrowMinErr('ng', 'badname'); }); + describe('triggering commit value on submit', function () { + it('should trigger update on form submit', function() { + var form = $compile( + '
' + + '' + + '
')(scope); + scope.$digest(); + + var inputElm = form.find('input').eq(0); + changeInputValue(inputElm, 'a'); + expect(scope.name).toEqual(undefined); + browserTrigger(form, 'submit'); + expect(scope.name).toEqual('a'); + dealoc(form); + }); + + it('should trigger update on form submit with nested forms', function() { + var form = $compile( + '
' + + '
' + + '' + + '
' + + '
')(scope); + scope.$digest(); + + var inputElm = form.find('input').eq(0); + changeInputValue(inputElm, 'a'); + expect(scope.name).toEqual(undefined); + browserTrigger(form, 'submit'); + expect(scope.name).toEqual('a'); + dealoc(form); + }); + + it('should trigger update before ng-submit is invoked', function() { + var form = $compile( + '
' + + '' + + '
')(scope); + scope.$digest(); + + var inputElm = form.find('input').eq(0); + changeInputValue(inputElm, 'a'); + scope.submit = jasmine.createSpy('submit').andCallFake(function() { + expect(scope.name).toEqual('a'); + }); + browserTrigger(form, 'submit'); + expect(scope.submit).toHaveBeenCalled(); + dealoc(form); + }); + }); describe('preventing default submission', function() {