Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

fix($parse): allow watching object/array literals #15274

Closed
wants to merge 1 commit into from

Conversation

jbedard
Copy link
Collaborator

@jbedard jbedard commented Oct 16, 2016

This moves the object/array-literal watching logic from $compile to $parse so it can by used by watchers anywhere (not only bindings).

By moving this to $parse we can potentially improve it more. Maybe pushing the equals check into expressionInputDirtyCheck for literals, then we can avoid creating the object and avoid calling equals on the full object each digest?

What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
bug fix

What is the current behavior? (You can also link to an open issue here)
watching object/array literals throws infdig if any inputs are non-simple values

What is the new behavior (if this is a feature change)?
watching object/array literals does what compile previously did to solve this

Does this PR introduce a breaking change?
no

Please check if the PR fulfills these requirements

@gkalpak
Copy link
Member

gkalpak commented Oct 16, 2016

This sounds more like a feature than a fix.

@gkalpak
Copy link
Member

gkalpak commented Oct 16, 2016

I wonder how much people would abuse this, without realizing it is inefficient :)


var initialValue = destination[scopeName] = parentGet(scope);
initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);

removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newValue, oldValue) {
if (oldValue === newValue) {
if (oldValue === initialValue || (deepWatch && equals(oldValue, initialValue))) {
if (equals(oldValue, initialValue)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is different that what we had before. equals(oldValue, initialValue) might be true even if oldValue !== initialValue for non-literals. In that case, we don't want to return.

This line shouldn't change afaict.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was hoping to remove all "deepWatch" logic from here. I can just revert it, but any other ideas?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have any better idea 😞

The thing is we need to call $onChanges synchronously (while the watchAction will only fire asynchronously), so we call $onChanges manually. When the watchAction does fire for the first time we need to determine if the value has changed - i.e. if initialValue is equal to oldValue in the $watch sense. That's why we need to differentiate between literals (compared using equals) and non-literals (compared using ===).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've undone this and added tests in a separate PR: #15300

@jbedard
Copy link
Collaborator Author

jbedard commented Oct 16, 2016

I think this is a fix because today $watch("{foo: bar}") works sometimes but not others. Basically if bar is a simple value it works, but as soon as bar becomes an object of any type it breaks.

That said... I think I would prefer a BC where we stop using equals for literals and instead use $watchGroup. So $watch("{a: x, b: y}") would essentially be $watchGroup(["x", "y"]) instead of a deep watch like today. This would improve performance when those objects are large and also stop invoking the watcher just because a child property (that we might not care about) changed.

@gkalpak gkalpak modified the milestones: Backlog2, Backlog Oct 18, 2016
@@ -3522,7 +3522,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
recordChanges(scopeName, newValue, oldValue);
destination[scopeName] = newValue;
}, deepWatch);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It probably makes sense to rename deepWatch now that isn't not used for deep-watching 😃

@gkalpak
Copy link
Member

gkalpak commented Oct 20, 2016

It still feels more like a feature to me. It is only by chance that "shallow" literals started working at some point. 😃

LGTM (but it would be good for someone else to take a look as well)

@petebacondarwin petebacondarwin modified the milestones: 1.6.x (post 1.6.0), Backlog Dec 5, 2016
@petebacondarwin petebacondarwin modified the milestones: 1.7.0, 1.6.x Jan 23, 2017
@jbedard
Copy link
Collaborator Author

jbedard commented Jan 27, 2017

I think I'd actually prefer to close this in favour of only doing #15301 in 1.7.

This PR makes literals consistent by expanding the deep-equals functionality of one-way-bindings to all watched literals. Where #15301 makes literals consistent by removing the deep-equals and instead (simple) watching only the variables within the literal. So we probably shouldn't expand the use of deep-equals if we plan to land #15301 and remove it...

@gkalpak
Copy link
Member

gkalpak commented Jan 27, 2017

Works for me. Feel free to go ahead and close this in favor of #15301.

@jbedard
Copy link
Collaborator Author

jbedard commented Jan 27, 2017

Closing then... also note that I think the first commit in #15301 could go into 1.6, but I'll comment on that there...

@jbedard jbedard closed this Jan 27, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants