-
Notifications
You must be signed in to change notification settings - Fork 27.4k
fix+perf($parse): allow watching array/object literal values, disable deep watch for one-way bindings #15301
Conversation
- the content of array/object literals is now watched for changes - if the content changes then a new array/object will be constructed
…r literals BREAKING CHANGE: Previously when a literal value was passed into a directive/component via one-way binding it would be watched with a deep watcher. For example, for `<my-component input="[a]">`, a new instance of the array would be passed into the directive/component (and trigger $onChanges) not only if `a` changed but also if any sub property of `a` changed such as `a.b` or `a.b.c.d.e` etc. This also means a new but equal value for `a` would NOT trigger such a change. Now literal values use a non-deep watch similar to other directive/component one-way bindings. Changes are only trigger when the value itself changes. Avoiding deep watchers for array/object literals will improve watcher performance of all literals passed as one-way bindings, especially those containing references to large/complex objects.
Seems like a good thing (it has to wait for 1.7.0 though). |
@@ -3522,7 +3522,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { | |||
} | |||
recordChanges(scopeName, newValue, oldValue); | |||
destination[scopeName] = newValue; | |||
}, deepWatch); |
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.
Deep watching never happens, but we still have a variable named deepWatch
. Isn't it a bit misleading?
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 let's change deepWatch
to isLiteral
.
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 think this looks good. I would like to see a test in compileSpec.js
that demonstrates that we are no longer deep watching - just for the record.
@@ -3522,7 +3522,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { | |||
} | |||
recordChanges(scopeName, newValue, oldValue); | |||
destination[scopeName] = newValue; | |||
}, deepWatch); |
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 let's change deepWatch
to isLiteral
.
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.
So I guess I shouldn't approve this since I have asked for two changes:
- rename
deepWatch
local variable toisLiteral
- add test to
compileSpec.js
1fecb6f
to
0706760
Compare
Renamed the var and added some tests. I think those should both be squashed with the second commit. Note that c175619 (first commit) can actually go into 1.6 since it only adds support for literal-watching to |
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 wonder how this change affects watching object literals with computed property names.
@@ -4259,6 +4259,78 @@ describe('$compile', function() { | |||
}); | |||
|
|||
|
|||
it('should trigger `$onChanges` for literal expressions when expression input value changes (simple value)', function() { |
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 you also add tests verifying that $onChanges
is not called when a property of the input value changes?
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.
Great. Thanks @jbedard
I will squash and merge. The first commit should probably be a feat
, right? And I agree that it can go into 1.6
The inputs of array/object literals are now watched for changes. If the an input changes then a new instance of the literal will be provided when the parsed expression is executed. Closes #15301
Avoiding deep watchers for array/object literals will improve watcher performance of all literals passed as one-way bindings, especially those containing references to large/complex objects. BREAKING CHANGE: Previously when a literal value was passed into a directive/component via one-way binding it would be watched with a deep watcher. For example, for `<my-component input="[a]">`, a new instance of the array would be passed into the directive/component (and trigger $onChanges) not only if `a` changed but also if any sub property of `a` changed such as `a.b` or `a.b.c.d.e` etc. This also means a new but equal value for `a` would NOT trigger such a change. Now literal values use an input-based watch similar to other directive/component one-way bindings. In this context inputs are the non-constant parts of the literal. In the example above the input would be `a`. Changes are only triggered when the inputs to the literal change. Closes #15301
The inputs of array/object literals are now watched for changes. If the an input changes then a new instance of the literal will be provided when the parsed expression is executed. Closes angular#15301
Avoiding deep watchers for array/object literals will improve watcher performance of all literals passed as one-way bindings, especially those containing references to large/complex objects. BREAKING CHANGE: Previously when a literal value was passed into a directive/component via one-way binding it would be watched with a deep watcher. For example, for `<my-component input="[a]">`, a new instance of the array would be passed into the directive/component (and trigger $onChanges) not only if `a` changed but also if any sub property of `a` changed such as `a.b` or `a.b.c.d.e` etc. This also means a new but equal value for `a` would NOT trigger such a change. Now literal values use an input-based watch similar to other directive/component one-way bindings. In this context inputs are the non-constant parts of the literal. In the example above the input would be `a`. Changes are only triggered when the inputs to the literal change. Closes angular#15301
I think this is a better alternative to #15274, but has a breaking change (which didn't break any tests!).
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
Bug fix, perf
What is the current behavior? (You can also link to an open issue here)
Watching literals doesn't always work, literals passed to components are deep watched to work around this.
What is the new behavior (if this is a feature change)?
Watching literals now does a watch of the inputs.
Does this PR introduce a breaking change?
YES - see below or second commit
Please check if the PR fulfills these requirements
BREAKING CHANGE (65ff861):
Previously when a literal value was passed into a directive/component via one-way binding it would be watched with a deep watcher.
For example, for
<my-component input="[a]">
, a new instance of the array would be passed into the directive/component (and trigger $onChanges) not only ifa
changed but also if any sub property ofa
changed such asa.b
ora.b.c.d.e
etc.This also means a new but equal value for
a
would NOT trigger such a change.Now literal values use a non-deep watch similar to other directive/component one-way bindings. Changes are only trigger when the value itself changes.
Avoiding deep watchers for array/object literals will improve watcher performance of all literals passed as one-way bindings, especially those containing references to large/complex objects.