-
Notifications
You must be signed in to change notification settings - Fork 27.4k
Conversation
@jbedard I rebased your PR #9006 and made several improvements and fixed some small bugs. I think this is pretty close to being mergable. The missing pieces are:
Please take a look at my changes and let me know if you see anything odd |
Looks like you didn't rebase the latest commit? I had a couple additional |
Yup. You are right. I'll update my PR later. I'm on cell so it's hard to read your diff but from what I've seen, I'm not quite sure how does the isBranching check make things any better. Can you shed some light on this? |
expect(watcherCalls).toBe(1); | ||
scope.$digest(); | ||
expect(filterCalls).toBe(1); | ||
expect(watcherCalls).toBe(1); |
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 really understand this test --- "should calculate the literal every single time", but asserting that the filter and watcher was only called once in two digests.
What are we actually testing here? That, because the inputs didn't change, we don't call the filter / consider the watch to have 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.
I think I pointed out the same. I'm going to take a closer look at the
tests before merging.
On Sep 15, 2014 4:18 PM, "Caitlin Potter" notifications@github.com wrote:
In test/ng/parseSpec.js:
$filterProvider.register('foo', valueFn(function(input) {
filterCalls++;
return input;
}));
var watcherCalls = 0;
scope.$watch($parse('{x: 1} | foo'), function(input) {
expect(input).toEqual({x:1});
watcherCalls++;
});
scope.$digest();
expect(filterCalls).toBe(1);
expect(watcherCalls).toBe(1);
scope.$digest();
expect(filterCalls).toBe(1);
expect(watcherCalls).toBe(1);
I don't really understand this test --- "should calculate the literal
every single time", but asserting that the filter and watcher was only
called once in two digests.What are we actually testing here? That, because the inputs didn't change,
we don't call the filter / consider the watch to have changed?—
Reply to this email directly or view it on GitHub
https://github.com/angular/angular.js/pull/9082/files#r17543950.
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.
The tests were also updated in the latest commit. Really I think all this test should do is...
expect($parse('{x: 1} | foo').constant).toBe(true);
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 test got moved down after the merge/cherry-pick, although I think it should be simplified like I mentioned above.
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 removed this dupe.
the other copy does contain the "constant" assertion. I kept the other assertions as they were. I think that they are useful.
The isBranching is set for AND/OR operators and is mainly to fix short-circuiting such as |
edb554a
to
d1b57df
Compare
@jbedard I pulled in the changes from your branch and cherry-picked them on top of this branch. The use of isBranching makes sense. I didn't make the connection when reading the code on tiny screen. As far as valueOf goes, we do need a different strategy because both of our solutions are incorrect. I was also thinking of keeping the primitive value around and comparing that. I'll give it a shot. |
expect(watcherCalls).toBe(1); | ||
})); | ||
it('should not treat constants passed to filters with externalInput as constants', inject(function($parse) { | ||
|
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.
And this one should just set the externalInput
flag on the foo
filter, and expect(parsed.constant).toBe(false)
.
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.
such expression would result in infinite digest though because we'd recreate the constant object during each reevaluation.
this could be fixed in the future, but I'm not going to deal with it now. instead I'll just remove this single spec.
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, didn't think of that. And it won't have the normal literal handling
because the literal is not the root...
In test/ng/parseSpec.js:
var watcherCalls = 0;
scope.$watch(parsed, function(input) {
expect(input).toEqual({x:1});
watcherCalls++;
});
scope.$digest();
expect(filterCalls).toBe(1);
expect(watcherCalls).toBe(1);
scope.$digest();
expect(filterCalls).toBe(1);
expect(watcherCalls).toBe(1);
}));
it('should not treat constants passed to filters with externalInput as constants', inject(function($parse) {
such expression would result in infinite digest though because we'd
recreate the constant object during each reevaluation.
this could be fixed in the future, but I'm not going to deal with it now.
instead I'll just remove this single spec.
—
Reply to this email directly or view it on GitHub
https://github.com/angular/angular.js/pull/9082/files#r17571465.
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.
such expression would result in infinite digest though because we'd
recreate the constant object during each reevaluation.
Actually, that sounds like a problem --- because it sounds like it could break peoples code. I might be missing something, but I don't think we want to not fix that
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 that is nothing new, but now it will only happen when the filter
also has the "externalInput" flag. I think the test was just bad...
For example $scope.$watch("[0] | filter")
would always cause this because
the output is a literal (recreated each digest) but is not constant because
of the filter.
On 15 September 2014 15:21, Caitlin Potter notifications@github.com wrote:
In test/ng/parseSpec.js:
var watcherCalls = 0;
scope.$watch(parsed, function(input) {
expect(input).toEqual({x:1});
watcherCalls++;
});
scope.$digest();
expect(filterCalls).toBe(1);
expect(watcherCalls).toBe(1);
scope.$digest();
expect(filterCalls).toBe(1);
expect(watcherCalls).toBe(1);
}));
it('should not treat constants passed to filters with externalInput as constants', inject(function($parse) {
such expression would result in infinite digest though because we'd
recreate the constant object during each reevaluation.Actually, that sounds like a problem --- because it sounds like it could
break peoples code. I might be missing something, but I don't think we want
to not fix that—
Reply to this email directly or view it on GitHub
https://github.com/angular/angular.js/pull/9082/files#r17572834.
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 has always been this way. so i don't think that it's worth fixing right now.
d1b57df
to
6bc090d
Compare
@jbedard I fixed the valueOf issue and added some more tests. PTAL |
var newInputValue = inputExpressions(scope); | ||
if (!expressionInputDirtyCheck(newInputValue, oldInputValue)) { | ||
lastResult = parsedExpression(scope); | ||
oldInputValue = newInputValue.valueOf(); |
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.
Does this need newInputValue &&
to handle null/undefined like the one 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.
yup. fixed
Other then the null/undefined comment everything LGTM. Except I had a few other ideas/questions but they could all wait:
|
f1b25c4
to
bbeeeb8
Compare
With this change, expressions like "firstName + ' ' + lastName | uppercase" will be analyzed and only the inputs for the expression will be watched (in this case "firstName" and "lastName"). Only when at least one of the inputs change, the expression will be evaluated. This change speeds up simple expressions like `firstName | noop` by 20% and more complex expressions like `startDate | date` by 2500%. BREAKING CHANGE: all filters are assumed to be stateless functions Previously it was a good practice to make all filters stateless, but now it's a requirement in order for the model change-observation to pick up all changes. If an existing filter is statefull, it can be flagged as such but keep in mind that this will result in a significant performance-penalty that will affect the $digest duration. To flag a filter as stateful do the following: myApp.filter('myFilter', function() { function myFilter(input) { ... }; myFilter.$stateful = true; return myFilter; }); Closes angular#9006 Closes angular#9082
I cleaned up the commits, squashed most of them and wrote a better commit message. To answer your questions
|
bbeeeb8
to
a9f87bd
Compare
With this change, expressions like "firstName + ' ' + lastName | uppercase" will be analyzed and only the inputs for the expression will be watched (in this case "firstName" and "lastName"). Only when at least one of the inputs change, the expression will be evaluated. This change speeds up simple expressions like `firstName | noop` by ~15% and more complex expressions like `startDate | date` by ~2500%. BREAKING CHANGE: all filters are assumed to be stateless functions Previously it was a good practice to make all filters stateless, but now it's a requirement in order for the model change-observation to pick up all changes. If an existing filter is statefull, it can be flagged as such but keep in mind that this will result in a significant performance-penalty that will affect the $digest duration. To flag a filter as stateful do the following: myApp.filter('myFilter', function() { function myFilter(input) { ... }; myFilter.$stateful = true; return myFilter; }); Closes angular#9006 Closes angular#9082
With this change, expressions like "firstName + ' ' + lastName | uppercase" will be analyzed and only the inputs for the expression will be watched (in this case "firstName" and "lastName"). Only when at least one of the inputs change, the expression will be evaluated. This change speeds up simple expressions like `firstName | noop` by ~15% and more complex expressions like `startDate | date` by ~2500%. BREAKING CHANGE: all filters are assumed to be stateless functions Previously it was a good practice to make all filters stateless, but now it's a requirement in order for the model change-observation to pick up all changes. If an existing filter is statefull, it can be flagged as such but keep in mind that this will result in a significant performance-penalty (or rather lost opportunity to benefit from a major perf improvement) that will affect the $digest duration. To flag a filter as stateful do the following: myApp.filter('myFilter', function() { function myFilter(input) { ... }; myFilter.$stateful = true; return myFilter; }); Closes angular#9006 Closes angular#9082
splitting the code into two chunks doesn't buy us anything and only makes the code harder to follow
a9f87bd
to
aaa8e7d
Compare
02dc2aa
to
fd2d6c0
Compare
improved version of #9006 (which was an improved version of #8942)