filterFilter compare custom objects in 1.3.6 #10464

Closed
m7r opened this Issue Dec 15, 2014 · 21 comments

Projects

None yet

5 participants

@m7r
Contributor
m7r commented Dec 15, 2014

First off all I am a big fan of the changed behavior as I can do finally filtering complex data as I could in angular 1.0.x

Only the default comparator should not return false if actual or expected is an object.

My use case are mongodb ObjectIds with a custom toString function.
From angular 1.0 to 1.3.5 the filterFilter could compare this custom object with a string or another ObjectId.

I know I can use my one comparator but i think the new comparator is a bit to picky.
A string like '[object' is in my opinion a user error.

WDYT?

@gkalpak
Member
gkalpak commented Dec 15, 2014

@m7r: There have been a couple of regressions reported and fixes are submitted for those, but I can't really tell if your usecase falls under one of those categories (as I can't quite understand what is the issue here).

Could you give an example (i.e. input, expected output, actual output) illustrating the problem/regression or better yet a live reproduction using plunkr / jsfiddle / ... ?

@m7r
Contributor
m7r commented Dec 15, 2014

@gkalpak http://plnkr.co/edit/fTbKNaot13K6v81xDwRL

Switch the angular version to see better what I mean.

@gkalpak
Member
gkalpak commented Dec 15, 2014

@m7r: I see what you mean. I personally don't think that the default comparator (which is supposed to match substrings case-insensitively) should match a string with an Object (and yes, that sounds like a good use-case for a custom comparator imo 😄), but let's see what others think.

/ping @caitp

@caitp
Contributor
caitp commented Dec 15, 2014

We have to support objects in the default comparator, for the case when no predicate is supplied.

However, this only applies when the two items being compared are both objects, it does not compare a string with an object.

@gkalpak
Member
gkalpak commented Dec 15, 2014

(Not sure what you mean by "predicate", so I assume "expression"; correct me if I'm wrong.)

If no predicate is supplied, then the original array is returned (as was the case in pre-1.3.6). We don't need the comparator for that (since there's nothing to compare with) ?

When two we have to compare two objects they are compared by deepCompare and not the comparator.

So, if I understand correctly, you agree that we shouldn't convert objects to strings for comparing against a string ?

Note, that an object might have a custom toString method, as in @m7r's use-case.

Another option might be to check if the object has a `toString()` method that is not Object's `toString()` method and use it if one is found.
@caitp
Contributor
caitp commented Dec 15, 2014

If no predicate is supplied, then the original array is returned (as was the case in pre-1.3.6). We don't need the comparator for that (since there's nothing to compare with) ?

No, that only applies if the predicate is undefined --- and that is actually the incorrect behaviour, we need to fix that.

(Predicate, historically, was one or more comparators --- reflecting properties of the object, or custom functions --- where the result of the filter would require each predicate comparator to return true --- you've sort of mangled this a bit with your refactoring)

@gkalpak
Member
gkalpak commented Dec 15, 2014

AFAIKT, predicate was one or more filtering functions (and there was also a comparator).
No there is the predicateFn :)

No, that only applies if the predicate is undefined

Hm...so, what do you mean by "no predicate is supplied" then ?

and that is actually the incorrect behaviour, we need to fix that.

Why is it incorrect ? What should happen instead.
(Note: We had a PR recently to make sure undefined is ignored, both as an expression and as a rpoperty value.)

@caitp
Contributor
caitp commented Dec 15, 2014

AFAIKT, predicate was one or more filtering functions (and there was also a comparator).
No there is the predicateFn :)

You're mistaken --- the predicate functions were either constructed based on input, or were already functions. There is predicateFn, but there used to be multiple predicateFns, not just one. So the idea is, for a generated predicate, if the properties it is comparing are objects, it needs to handle them specially.

Why is it incorrect ? What should happen instead.
(Note: We had a PR recently to make sure undefined is ignored, both as an expression and as a rpoperty value.)

Because we need to reverse the array if it's asked for

Hm...so, what do you mean by "no predicate is supplied" then ?

If the value is null, you'll get a very different behaviour

@gkalpak
Member
gkalpak commented Dec 15, 2014

Why is it incorrect ? What should happen instead.
(Note: We had a PR recently to make sure undefined is ignored, both as an expression and as a property value.)

Because we need to reverse the array if it's asked for

Hm...not sure why/how it can be asked to reverse the array. filterFilter should not change the order.

Hm...so, what do you mean by "no predicate is supplied" then ?

If the value is null, you'll get a very different behaviour

Could you give an example ? We are probably talking about different things, because I see the same behaviour whether the expression is null or undefined.

@caitp
Contributor
caitp commented Dec 15, 2014

Hm...not sure why/how it can be asked to reverse the array. filterFilter should not change the order.

You're right, thinking of orderBy

@gkalpak
Member
gkalpak commented Dec 16, 2014

So, @caitp, regarding the original question, I see 3 options:

  1. comparator(<Object>, <Primitive>) ---> always false
    This is the current behaviour.
  2. comparator(<Object>, <Primitive>) ---> comparator(<Object>.toString(), <Primitive>)
    Will incorrectly allow unexpected matches (e.g. the string object to match an Object).
  3. comparator(<Object>, <Primitive>) ---> (<Object>.toString === Object.prototype.toString) ? false : /* Same as option (2) */
    Possibly combining the best of two worlds (but at a tiny extra cost).

Which on should we go by ?

@petebacondarwin
Member

@gkalpak - so what you are saying is that in option 3, it will return false unless the object has overridden the meaning of toString? This is kind of nice because it will also work well with Date objects.

Let's go with option 3

@petebacondarwin petebacondarwin added this to the 1.3.9 milestone Dec 17, 2014
@petebacondarwin
Member

I have added the PRs plz! label to this issue as this is a fairly straightforward fix. Please make sure you do the following:

  • comment on the issue to say that you are working on this issue
  • sign the CLA
  • add unit tests that demonstrate this problem
  • implement option 3 to fix the unit tests - I would break up the current if statement that checks the type of the obj param
  • update the docs to make it clear what happens if you are trying to match a string to an object
  • submit a pull request with a well written commit message
@gkalpak
Member
gkalpak commented Dec 17, 2014

@petebacondarwin: Do you mean we should leave this for a community member's first contribution (as it is a "good first PR") or do you want me to submit a PR ?

@petebacondarwin
Member

@gkalpak - if you have time go for it. I just wanted to put it out there as an option.

@m7r
Contributor
m7r commented Dec 17, 2014

@gkalpak I can look into it later today too.

@petebacondarwin
Member

@gkalpak - let's give this one to @m7r - then perhaps you could review it?
Thanks.

@m7r m7r pushed a commit to m7r/angular.js that referenced this issue Dec 17, 2014
Martin R. Hufsky feat(filterFilter): compare object with own `toString` to primitive
Closes #10464
07c708e
@m7r
Contributor
m7r commented Dec 17, 2014

@gkalpak could you have a look at the changes please?
I am not a native speaker are the comments and docs ok?
FYI the !isFunction is necessary for objects without prototype.

@gkalpak
Member
gkalpak commented Dec 17, 2014

@m7r: Sure, I'll take a look tomorrow.

@gkalpak
Member
gkalpak commented Dec 18, 2014

@m7r: I left some comments. They're mostly test-related; implementation-wise it looks good.

@m7r m7r pushed a commit to m7r/angular.js that referenced this issue Dec 18, 2014
Martin R. Hufsky feat(filterFilter): compare object with custom `toString()` to primitive
Closes #10464
2209e16
@m7r
Contributor
m7r commented Dec 18, 2014

@gkalpak Thanks for the comments I have updated my commit accordingly.

@m7r m7r pushed a commit to m7r/angular.js that referenced this issue Dec 22, 2014
Martin R. Hufsky feat(filterFilter): compare object with custom `toString()` to primitive
Closes #10464
8ae32f7
@m7r m7r added a commit to m7r/angular.js that referenced this issue Dec 22, 2014
@m7r m7r feat(filterFilter): compare object with custom `toString()` to primitive
Closes #10464
309b0b0
@m7r m7r added a commit to m7r/angular.js that referenced this issue Dec 23, 2014
@m7r m7r feat(filterFilter): compare object with custom `toString()` to primitive
Closes #10464
c157a25
@hansmaad hansmaad pushed a commit to hansmaad/angular.js that referenced this issue Mar 10, 2015
@m7r m7r + hansmaad feat(filterFilter): compare object with custom `toString()` to primitive
Closes #10464
Closes #10548
cf6b276
@netman92 netman92 added a commit to netman92/angular.js that referenced this issue Aug 8, 2015
@m7r @netman92 m7r + netman92 feat(filterFilter): compare object with custom `toString()` to primitive
Closes #10464
Closes #10548
b7a922a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment