-
Notifications
You must be signed in to change notification settings - Fork 27.4k
feat(ngModelOptions): support submit trigger #7116
Conversation
Thanks for the PR! Please check the items below to help us merge this faster. See the contributing docs for more information.
If you need to make changes to your pull request, you can update the commit with Thanks again for your help! |
@shahata - where did we get to on this? I am back from vacation and would like to move this forward. |
@petebacondarwin - cool, let me know what you think. There wasn't any progress on this while you've been away :) |
I had a chat with the team about form submit stuff today. They are happy to get this in but wanted the event to be "private", i.e. not a public API and should be called I have a slightly uneasy feeling about this second implementation, regarding the fact that certain events always update the pendingValue even if they are not on the updateOn list. It may be nothing but I need to think about this a bit more. But generally I like the idea of removing duplication. |
Sure. I'll update the PR to make the |
done |
I don't think we actually need to use events for this form submit stuff! The FormController already has access to all child inputs, because their NgModelControllers add them to any enclosing form. So we can just use that to "broadcast" to the child NgModelControllers. |
You are right, I decided to leave the event just so users will be able to trigger the submit flow regardless of the form submit. Since we decided to make this event private, it probably doesn't make sense to keep using an event. I updated the PR accordingly and now we use a |
@@ -635,6 +635,7 @@ describe('input', function() { | |||
'<input type="text" ng-model="name" name="alias" '+ | |||
'ng-model-options="{ updateOn: \'blur\' }"'+ | |||
'/>'); | |||
scope.$digest(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this really needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I remove it the test fails...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, so the problem is that the first digest is synchronizing the pendingValue
to the value of the model. If you don't have a digest before the first "change" event to update the pendingValue
then when the first digest is called it doesn't realise that pendingValue
had already been set and overrides it with undefined
. See https://github.com/shahata/angular.js/blob/updateOnSubmit2/src/ng/directive/input.js#L1869-L1872
if (ctrl.$viewValue !== value) {
ctrl.$viewValue = pendingValue = value;
ctrl.$render();
}
I think it might be fair to expect a digest always to occur before any "change" event can occur on the input...?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly, the first digest cycle needs to happen before anything is changed by the user, otherwise it will just reset the $viewValue
to the current value of the model, overriding any change that was already done by the user. This is the case in ngModelOptions
regardless of this PR, it is just emphasized by the use of pendingValue
.
I think it is a not only fair to expect this - more over, I believe that if we don't do this in tests it is kind of "cheating" since it isn't realistic that the digest doesn't happen before the change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. That makes sense. We should then move the digest call into the compileInput
helper method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, I will do this in a separate commit.
@IgorMinar - Thanks for your comments. I've updated the PR accordingly. |
I'm proposing these changes:
optionally it might then make sense to:
|
So effectively compared to the current PR:
|
When a debounce resolves (or a non-debounced sync event occurs), it just calls
When the form is submitted, then FormController will just call |
@IgorMinar @petebacondarwin - Thanks! I've just pushed another commit with your proposed changes. I hope I understood correctly what you meant. :) The whole thing looks much clearer now in my opinion. The only problem I had with your suggestions is with getting rid of This means that the name of the method needs to be the same in Also, I think I like |
I would go with changing FormController's method to |
done |
Looks good to me. I don't like the name of |
I agree about the |
Alternatively, |
Great! Renamed to |
*/ | ||
this.$commitViewValue = function() { | ||
var value = ctrl.$viewValue; | ||
ctrl.$$lastCommitedViewValue = value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should also cancel any pending task with $timeout.cancel(pendingDebounce);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right, adding
@petebacondarwin I was about to suggest switching form controller to use |
@@ -1863,7 +1860,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ | |||
} | |||
|
|||
if (ctrl.$viewValue !== value) { | |||
ctrl.$viewValue = value; | |||
ctrl.$viewValue = ctrl.$$lastCommitedViewValue = value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a typo through out this diff. $$lastCommitedViewValue
should be $$lastCommittedViewValue
(note t
-> tt
)
To summarize, we are now exposing apis with these names:
I'd like to unify the names if possible because now we are using two concepts to refer to the same thing: 1/ "update" and 2/ "commit". Actually there is also 3/ "change" for "$$debounceViewChange". If we were to stick with things as they are we should at least rename Going beyond that can we somehow unify the "update" and "commit" names so that we use only one or the other but not both? Some suggestions:
|
other than these small things this change looks great |
(oh and we need a better commit message that summarizes the changes and describes breaking changes if there are any, but let's resolve the other issues first) |
Renamed |
The current commit message is just a placeholder of course. Currently can't think of any breaking changes, but I'm possibly missing something. |
I know @IgorMinar was not keen on
In this configuration, the view value can be set and reset, while update is a single concept which refers to the model value being updated to match the parsed current view value, which can be debounced if necessary. |
move responsibility for pending and debouncing model updates into ngModeController by letting input dierctives pass all view updates to $setViewValue, where they will remain pending until an updateOn trigger occurs and debounced. introducing a new api ($commitViewValue) which allows to flush pending or debounced updates so that they will take place immidiately. BREAKING CHANGE: NgModelController.$cancelUpdate was renamed to NgModelController.$rollbackViewValue To migrate the code follow the example below: Before: $scope.resetWithCancel = function (e) { if (e.keyCode == 27) { $scope.myForm.myInput1.$cancelUpdate(); $scope.myValue = ''; } }; After: $scope.resetWithCancel = function (e) { if (e.keyCode == 27) { $scope.myForm.myInput1.$rollbackViewValue(); $scope.myValue = ''; } }
use the new NgModelController.$commitViewValue method in order to make sure that all pending and debounced updates are flushed immediately in case the enclosing for is submitted. Closes #7017
@IgorMinar @petebacondarwin - I rebased and updated the comments on my commits. I believe we should separate this to three different commits. Let me know if you prefer I squash all three into one. |
Landed as a0ae07b |
This is another attempt to solve #7017 from a different direction. This one actually moves a big part of the
ngModelOptions
mechanism out of the variousinput
directives and intongModelController
which is a better approach in my opinion.