mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-04-13 22:39:59 +08:00
feat(FormController): commit $viewValue of all child controls when form is submitted
Use the new `NgModelController.$commitViewValue()` method to commit the `$viewValue` on all the child controls (including nested `ngForm`s) when the form receives the `submit` event. This will happen immediately, overriding any `updateOn` and `debounce` settings from `ngModelOptions`. If you wish to access the committed `$modelValue`s then you can use the `ngSubmit` directive to provide a handler. Don't use `ngClick` on the submit button, as this handler would be called before the pending `$viewValue`s have been committed. Closes #7017
This commit is contained in:
committed by
Peter Bacon Darwin
parent
adfc322b04
commit
a0ae07bd4e
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
'<form name="test" ng-model-options="{ updateOn: \'\' }" >' +
|
||||
'<input type="text" ng-model="name" />' +
|
||||
'</form>')(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(
|
||||
'<form name="test" ng-model-options="{ updateOn: \'\' }" >' +
|
||||
'<div class="ng-form" name="child">' +
|
||||
'<input type="text" ng-model="name" />' +
|
||||
'</div>' +
|
||||
'</form>')(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(
|
||||
'<form name="test" ng-submit="submit()" ' +
|
||||
'ng-model-options="{ updateOn: \'\' }" >' +
|
||||
'<input type="text" ng-model="name" />' +
|
||||
'</form>')(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() {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user