-
Notifications
You must be signed in to change notification settings - Fork 27.4k
fix(ngModelOptions): preserve context of getter/setters #10136
Conversation
Thanks for your pull request. It looks like this may be your first contribution to a Google open source project, in which case you'll need to sign a Contributor License Agreement (CLA) at https://cla.developers.google.com/. If you've already signed a CLA, it's possible we don't have your GitHub username or you're using a different email address. Check the information on your CLA or see this help article on setting the email on your git commits. Once you've done that, please reply here to let us know. If you signed the CLA as a corporation, please let us know the company's name. |
oh googlebot, you so crazy |
CLAs look good, thanks Bri Bri! |
pendingDebounce = null, | ||
ctrl = this; | ||
|
||
var ngModelGet = function ngModelGet() { | ||
var modelValue = parsedNgModel($scope); | ||
if (ctrl.$options && ctrl.$options.getterSetter && isFunction(modelValue)) { | ||
modelValue = modelValue(); | ||
modelValue = $scope.$eval($attr.ngModel + '()'); |
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 feel just a tiny bit bad about this, but I think perf-wise and breaking change-wise it's the least harmful.
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.
Uh, maybe I'm reading this wrong, but this will invoke $parse
constantly... this seems like quite a performance risk, no?
Storing the parsed expression like @jbedard proposed is similar but only involves calling $parse
once, on init, like how the non-getter/setter version of ngModel works.
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.
$parse
does cache it by the string, but I agree, this does still add a few calls ($eval + $parse) and a string concat to the watcher...
Another question... do we intentionally support the ngModelOptions object being modified after
My comment about changing |
Nope. I agree about this being a potential source of headaches. I think getter/setters already gives devs enough dynamicity. |
I think something a little closer to jbedard@3392351 would be nice then... |
@jbedard – nice!
I think if we continue discussion down this path we should have a benchmark for it. |
if (ctrl.$options && ctrl.$options.getterSetter && ctrl.$options.getterSetterContext) { | ||
// Use the provided context expression to specify the context used when invoking the | ||
// getter/setter function | ||
parsedNgModelContext = $parse(ctrl.$options.getterSetterContext); |
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 unused
The "trick" of adding But if you can make the error message helpful in that case, maybe it's OK? |
Amusingly, something else like |
:| We could do |
Actually what I said in that last comment won't work. Doing I've updated mine to be not as breaking jbedard@081195a. The main advantage is that it only checks Sorry if I'm spamming this PR. Should I open another one instead? |
I don't think your updated PR handles the weird cases I was talking about... the test passes, but only because your ternary expression is wrapped in brackets 😃 In any case, I think the examples I provided were pretty contrived. 99.999% of Unless of course someone can come up with a non-contrived counter example where this won't work, because I still have an uneasy feeling I can't quite shake... |
(tentatively moving this to the next milestone --- rearrange at your discretion) |
I like the approach in jbedard@081195a. I've updated my PR accordingly, and made the tests around this feature more explicit. I'm not interested in supporting weird ternary expressions for |
0e94771
to
d0a2f74
Compare
That approach will also help with #9609 by removing the wrapper and options/getterSetter check from the watch function. If you want to simplify it one step further, with an additional breaking change, then jbedard@3392351 is even simpler where getterSetter mode only supports getterSetter functions and no longer supports plain values. I think this makes things simpler, but it is a bigger breaking change. |
otherwise, LGTM |
Many thanks to @NevilleS and @jbedard for collaborating with me on a solution to this! Closes angular#9394 Closes angular#9865 BREAKING CHANGE: previously, ngModel invoked getter/setters in the global context. For example: ```js <input ng-model="model.value" ng-model-options="{ getterSetter: true }"> ``` would previously invoke `model.value()` in the global context. Now, ngModel invokes `value` with `model` as the context. It's unlikely that real apps relied on this behavior. If they did they can use `.bind` to explicilty bind a getter/getter to the global context, or just reference globals normally without `this`.
Landed as bb4d3b7. Thanks everyone! 👯 |
Closes #9394
BREAKING CHANGE: previously, ngModel invoked getter/setters in the global context.
For example:
would previously invoke
model.value()
in the global context.Now, ngModel invokes
value
withmodel
as the context.It's unlikely that real apps relied on this behavior. If they did they can use
.bind
to expliciltybind a getter/getter to the global context, or just reference globals normally without
this
.