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

fix(filterFilter): Fix filtering using an object expression when the filter value is undefined #10424

Closed
wants to merge 1 commit into from

Conversation

caseyhoward
Copy link

Fixes #10419

@googlebot
Copy link

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.

@@ -151,6 +151,10 @@ function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
comparator = equals;
} else if (!isFunction(comparator)) {
comparator = function(actual, expected) {
if (angular.isUndefined(expected)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

no need for the angular.

@caseyhoward
Copy link
Author

I agree with the changes you suggested and created a new pull request #10427 (not sure if that was the correct thing to do).

I also added a tests for having objects at a deeper level.

@googlebot
Copy link

We found a Contributor License Agreement for you (the sender of this pull request) and all commit authors, but as best as we can tell these commits were authored by someone else. If that's the case, please add them to this pull request and have them confirm that they're okay with these commits being contributed to Google. If we're mistaken and you did author these commits, just reply here to confirm.

@caitp
Copy link
Contributor

caitp commented Dec 11, 2014

I agree with the changes you suggested and created a new pull request #10427 (not sure if that was the correct thing to do).

In the future, please just push the commit to the same branch, it will update the pull request.

@caitp
Copy link
Contributor

caitp commented Dec 11, 2014

In fact, you can still do that now and close the newer PR, it will be easier

@caseyhoward
Copy link
Author

Ok, I'll do that. I just wasn't sure if you wanted it in multiple commits or not.

@caitp
Copy link
Contributor

caitp commented Dec 11, 2014

It's a single-commit if you squash it together and force push, which we do when merging anyway

@caseyhoward
Copy link
Author

Ok, thanks. I was wondering how you guys merged things. I updated it.

@caitp
Copy link
Contributor

caitp commented Dec 11, 2014

Looks good to me --- you need to sign the CLA and then we can land this. I hope that's not a problem

@caseyhoward
Copy link
Author

@googlebot I did author the commit

@caitp
Copy link
Contributor

caitp commented Dec 11, 2014

@caseyhoward it is confused because you submitted a pull request with commits pushed to a different user/org. But I see that you have signed the CLA, so it is okay.


it('should not filter when property is undefined', function() {
var items = [{name: 'a'}, {name: 'abc'}, {deep: {name: 'abc'}}];
expect(filter(items, {deep: {name: undefined}})).toEqual([{deep: {name: 'abc'}}]);
Copy link
Member

Choose a reason for hiding this comment

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

I think those two test-cases could be on the same it block. (wdyt @caitp ?)

Copy link
Member

Choose a reason for hiding this comment

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

It would be good to have one more test-case for matching an non-primitive property. E.g. something like:

var items = [{deep: {name: {first: 'John', last: 'Doe'}}}];
expect(filter(items, {deep: {name: undefined}})).toEqual([items[0]]);

(It could as well be incorporated into the above test-case.)

@gkalpak
Copy link
Member

gkalpak commented Dec 12, 2014

@caitp: There are basically two options, so we need to decide which one we'd like to go with:

  1. Let the comparator decide if an undefined property matches against an item's property and have the default comparator decide that undefined matched anything.
  2. Ignore propeties of the expression object that have a value of undefined.

If we decide to go with option (1), then this PR is fine.
If we decide to go with option (2), then we need a different implementation.

@gkalpak
Copy link
Member

gkalpak commented Dec 12, 2014

Regarding my previous comment: The old implementation actually implemented option (2) (ignoring expression properties with a type of undefined).

@caitp
Copy link
Contributor

caitp commented Dec 12, 2014

I think version 2 is a breaking change, because the version which uses equals() would be affected by that (unless I'm misreading you)

@gkalpak
Copy link
Member

gkalpak commented Dec 12, 2014

Considering that option 2 was the one implemented by the pre-1.3.6 version of filterFilter, I don't see how it would be a breakign change.

It would probably be the reasonable thing to do, but not a breaking change.

@caitp
Copy link
Contributor

caitp commented Dec 12, 2014

I don't see how it was implemented pre-1.3.6 --- the comparator functions differ between objectEquality mode and custom predicate modes, if the change is implemented before the comparator is called, then it affects objectEquality mode too --- which is incorrect. It is a breaking change to do it that way

@gkalpak
Copy link
Member

gkalpak commented Dec 12, 2014

I don't see how it was implemented pre-1.3.6

pre-1.3.6 expression properties of type undefined were ignored (totally ignored, as if they didn't existed). This is unrelated to the comparator (default, sctrict or custom). I.e. {prop: undefined} was treated as {}.

if the change is implemented before the comparator is called, then it affects objectEquality mode too

It does. It also affects custom comparators (e.g. I might want a comparator that matches undefined with null or whatever). This is incorrect (imo), but it is how it was implemented pre-1.3.6.

which is incorrect. It is a breaking change to do it that way

Unfortunately, we have to choose only one of: "Being correct (implementation-wise)" and "not being a breaking change".

@caitp
Copy link
Contributor

caitp commented Dec 12, 2014

Unfortunately, for minor releases, we have to choose "not being a breaking change" --- we can't keep doing these "break peoples apps in a patch release" things :\

@gkalpak
Copy link
Member

gkalpak commented Dec 12, 2014

So, this PR is not good for merging. @caseyhoward: since you worked on this one, would you like to submit another PR that implements option (2) ? If not, I can submit it sometime tomorrow.

Just to make sure it's clear: We need an implementation that treats undefined properties of the expression object as if they were not existing at all.

@caitp
Copy link
Contributor

caitp commented Dec 12, 2014

if (typeof expression[path] === 'undefined') return;
I guess this is what we took out

@gkalpak
Copy link
Member

gkalpak commented Dec 12, 2014

This is the line that "did it" on pre-1.3.6. This is not what we took out. We re-implemented the logic, because (as you can see), it only made predicates for the 1st level of expression's properties (=== no proper handling of deep objects).

@gkalpak
Copy link
Member

gkalpak commented Dec 12, 2014

@caseyhoward: If you try to tackle this (without giving it much thought), this is what I think should need changing:

  • Line 120:

    if (!isArray(array)) return array;   --->   if (!isArray(array) || !isDefined(expression)) return array;
  • Line 199:

    if (isFunction(expectedVal)) {   --->   if (isUndefined(expectedVal) || isFunction(expectedVal)) {

@@ -196,7 +196,7 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp) {
} else if (expectedType === 'object') {
for (key in expected) {
var expectedVal = expected[key];
if (isFunction(expectedVal)) {
if (isFunction(expectedVal) || isUndefined(expectedVal)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This broke things in our project. It changed the behaviour of $filter. I don't think it is OK to change the behaviour in minor releases.

Choose a reason for hiding this comment

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

@zuzusik I was under the impression that c62fa6b was only merged to 1.4.x beta so a release where we've got breaking changes. Do you see it on any of the 1.3.x branches as well?

Copy link
Contributor

Choose a reason for hiding this comment

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

@pkozlowski-opensource It was successfully landed to 1.3.15, look at the changelog

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

@zuzusik - This change was actually fixing a regression that occurred between 1.3.5 and 1.3.6 (see #10419). I am sorry that it took so long for us to fix this regression that you started using the filter in its regressed state.

netman92 pushed a commit to netman92/angular.js that referenced this pull request Aug 8, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Filter behavior change in 1.3.6
9 participants