-
Notifications
You must be signed in to change notification settings - Fork 27.5k
wip: feat(ngModel): run formatters / setModelValue #16237
Conversation
src/ng/directive/ngModel.js
Outdated
|
||
function ngModelWatchAction(modelValue) { | ||
// if scope model value and ngModel value are out of sync | ||
// TODO(perf): why not move this to the action fn? |
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.
Can delete this now
src/ng/directive/ngModel.js
Outdated
|
||
function ngModelWatchAction(modelValue) { | ||
// if scope model value and ngModel value are out of sync | ||
// TODO(perf): why not move this to the action fn? | ||
if (modelValue !== ctrl.$modelValue && |
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.
Can this be deleted now?
@@ -921,7 +924,8 @@ function setupModelWatcher(ctrl) { | |||
} | |||
|
|||
return modelValue; |
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.
Can this be deleted now?
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 guess we still need to return this because we need the digest to cycle again if the model value has actually changed...?
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.
Yeah this comment is outdated now. This was from when this was moved to the listener fn...
4eede61
to
09f501d
Compare
@jbedard I had to revert the code that was moved to the watchActionFn, because it caused test failures. Specifcally, it caused failures when the model was updated inside ngChange or the model setter - so I think the way it's done is necessary. |
What about something like |
Hm, that sounds like a good in-between option. It' doesn't have a public equivalent for view -> model but $$parseAndValidate could be exposed if necessary. |
09f501d
to
d00ed40
Compare
Generally I prefer to keep the API consistent (it makes it much easier to wrap your head around), but in this case BTW, I agree it makes sense to tie |
Î'll update the PR then. Regarding $$updateEmptyClasses(), I thought about it again, and think it's okay that this is set before $render() is called. It's also more of an issue if we had a $format() function, because that would not have called render. |
d00ed40
to
29256ce
Compare
Updated. |
29256ce
to
dc8a725
Compare
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.
Left some docs-related comments.
Otherwise LGTM 👍
src/ng/directive/ngModel.js
Outdated
(ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue) | ||
// checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator | ||
// eslint-disable-next-line no-self-compare | ||
(ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue) |
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.
Nice try! But the indentation i still off 😛
src/ng/directive/ngModel.js
Outdated
* @description | ||
* | ||
* Runs the model -> view pipeline on the current | ||
* {@link ngModel.NgModelController#$modelValue $modelValue} |
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.
Missing .
at the end.
src/ng/directive/ngModel.js
Outdated
* Application developers usually do not have to call this function themselves. | ||
* | ||
* This function can be used when the $viewValue or the rendered DOM value of the control should | ||
* be updated after a user 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.
It is not necessarily about user input. The way I understand it, it is for when app state has changed in a way that may affect formatting. (OK, I admit most of the time the state changes as a result of user input, but still... 😛)
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.
That's right. This is is just the most common case. Another would be that the formatters have changed but the model hasn't. If the model changes, the normal model -> view code should be enough. Unless someone changes the $modelValue directly, which is not recommended. I'll make this a it more generic.
src/ng/directive/ngModel.js
Outdated
* be updated after a user input. | ||
* | ||
* For example, consider a text input with an autocomplete list (for fruit, where the items are | ||
* objects with a name and an id. |
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.
Missing closing )
.
src/ng/directive/ngModel.js
Outdated
* For example, consider a text input with an autocomplete list (for fruit, where the items are | ||
* objects with a name and an id. | ||
* A user enters `ap` and then selects `Apricot` from the list. | ||
* Based on this, the autocomplete widget will call $setViewValue({name: 'Apricot', id: 443}), |
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.
It is better to wrap code in `. (E.g. $setViewValue(...)
here or ctrl.$processModelValue()
and $viewValue
below.)
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.
Do you wrap in inline code blocks? It looks like your formatting is a bit off
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.
Apparently if you want to escape `, you need to place the \
before, not after the ` 😇
src/ng/directive/ngModel.js
Outdated
* but the rendered value will still be `ap`. | ||
* The widget can then call ctrl.$processModelValue() to run the model -> view | ||
* pipeline again, which includes a formatter that converts the object into the string `Apricot` | ||
* which is set to the $viewValue and rendered in the DOM. |
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.
Consider avoiding two which
clauses. E.g. something line:
The widget can then call
ctrl.$processModelValue()
to run the model -> view pipeline, which will use a formatter to convert the object to the stringApricot
, update the$viewValue
and finally rendered it in the DOM.
src/ng/directive/ngModel.js
Outdated
|
||
ngModel.$parsers.push(function(value) { | ||
if (angular.isString(value)) { | ||
return scope.items.find(function(item) { |
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.
This is not ES5-compatible 😁
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.
Do we care? This only affects IE for core support, but I don't think we make guarantees for the docs
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 don't feel too strongly, but it would be good for the docs to show code that works in all supportd browsers. It would also be a first (afaik).
src/ng/directive/ngModel.js
Outdated
</file> | ||
<file name="index.html"> | ||
<div style="display: flex;"> | ||
<div margin-right: 30px;"> |
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.
Forgot about style
😃
src/ng/directive/ngModel.js
Outdated
<input ng-model="val" process-model /> | ||
|
||
<ul> | ||
<li ng-repeat="item in items | filter:val"><button ng-click="select(item)">{{item.name}}</li> |
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.
Ehem...relying on a method set on the scope by a directive on a different element doesn't sound like a good practice 😉
Maybe move all relevant code into the directive's template.
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.
It's just a POC that doesn't show correct encapsulation.
I thought about putting it in the directive's template but then it gets more complex too. I'd have to create another directive for the ngModel formatters / parsers, or search for the input directive in the autocomplete directive ... no child selectors in AngularJS unfortunately. Something like this: https://plnkr.co/edit/OOSjPSQag2huO6gtLFgg?p=preview
Idk if it's worth the complexity
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 was having something like this in mind: https://plnkr.co/edit/AeOA4eMGGMONSTrLinH2?p=preview
(TBH, I find this whole usecase weird. It sounds like an unnecessary complicated way to create an autocomplete 😃)
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.
Well, that's less complex and shows better encapsulation. I'll take it. But for the record, I don't think a real world autocomplete would be successful with a hard-coded input element. At least not for me ;)
You know the customer is always right, and apparently many devs implement autocomplete like this.
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.
Updated with a small tweak
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've fixed the typos and commented on the more tricky stuff
src/ng/directive/ngModel.js
Outdated
* Application developers usually do not have to call this function themselves. | ||
* | ||
* This function can be used when the $viewValue or the rendered DOM value of the control should | ||
* be updated after a user 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.
That's right. This is is just the most common case. Another would be that the formatters have changed but the model hasn't. If the model changes, the normal model -> view code should be enough. Unless someone changes the $modelValue directly, which is not recommended. I'll make this a it more generic.
src/ng/directive/ngModel.js
Outdated
* For example, consider a text input with an autocomplete list (for fruit, where the items are | ||
* objects with a name and an id. | ||
* A user enters `ap` and then selects `Apricot` from the list. | ||
* Based on this, the autocomplete widget will call $setViewValue({name: 'Apricot', id: 443}), |
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.
Do you wrap in inline code blocks? It looks like your formatting is a bit off
src/ng/directive/ngModel.js
Outdated
|
||
ngModel.$parsers.push(function(value) { | ||
if (angular.isString(value)) { | ||
return scope.items.find(function(item) { |
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.
Do we care? This only affects IE for core support, but I don't think we make guarantees for the docs
src/ng/directive/ngModel.js
Outdated
<input ng-model="val" process-model /> | ||
|
||
<ul> | ||
<li ng-repeat="item in items | filter:val"><button ng-click="select(item)">{{item.name}}</li> |
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.
It's just a POC that doesn't show correct encapsulation.
I thought about putting it in the directive's template but then it gets more complex too. I'd have to create another directive for the ngModel formatters / parsers, or search for the input directive in the autocomplete directive ... no child selectors in AngularJS unfortunately. Something like this: https://plnkr.co/edit/OOSjPSQag2huO6gtLFgg?p=preview
Idk if it's worth the complexity
1ee7a7f
to
6f08ae8
Compare
@@ -921,7 +924,8 @@ function setupModelWatcher(ctrl) { | |||
} | |||
|
|||
return modelValue; |
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 guess we still need to return this because we need the digest to cycle again if the model value has actually changed...?
6f08ae8
to
e895e27
Compare
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
feature
What is the current behavior? (You can also link to an open issue here)
There's currently no way to manually run the model -> view pipeline / formatters
What is the new behavior (if this is a feature change)?
An API to run the whole pipeline / the formatters is introduced.
Does this PR introduce a breaking change?
No
Please check if the PR fulfills these requirements
Other information:
The PR includes both a
$format
and a$setModelValue
function. I think only one is needed, we just need to decide which one.$format:
$setModelValue
Both methods can handle the basic case, where an app developer wants to run the formatters the view -> model pipeline has been run, see #3407 or #5221
I personally tend to introduce
$format
as it has the smaller surface area and introduces fewer side effects. The full model -> view pipeline is not really needed for most cases.