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

Commit a75537d

Browse files
gkalpakcaitp
authored andcommitted
fix(filterFilter): don't match primitive sub-expressions against any prop
Basically, implement the logic detailed in the 2nd point of #9757 (comment)
1 parent 5ced914 commit a75537d

File tree

2 files changed

+42
-15
lines changed

2 files changed

+42
-15
lines changed

src/ng/filter/filter.js

+23-14
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,20 @@ function filterFilter() {
120120
if (!isArray(array)) return array;
121121

122122
var predicateFn;
123+
var matchAgainstAnyProp;
123124

124125
switch (typeof expression) {
125126
case 'function':
126127
predicateFn = expression;
127128
break;
128129
case 'boolean':
129130
case 'number':
130-
case 'object':
131131
case 'string':
132-
predicateFn = createPredicateFn(expression, comparator);
132+
matchAgainstAnyProp = true;
133+
//jshint -W086
134+
case 'object':
135+
//jshint +W086
136+
predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
133137
break;
134138
default:
135139
return array;
@@ -140,51 +144,56 @@ function filterFilter() {
140144
}
141145

142146
// Helper functions for `filterFilter`
143-
function createPredicateFn(expression, comparator) {
147+
function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
144148
var predicateFn;
145149

146150
if (comparator === true) {
147151
comparator = equals;
148152
} else if (!isFunction(comparator)) {
149153
comparator = function(actual, expected) {
154+
if (isObject(actual) || isObject(expected)) {
155+
// Prevent an object to be considered equal to a string like `'[object'`
156+
return false;
157+
}
158+
150159
actual = lowercase('' + actual);
151160
expected = lowercase('' + expected);
152161
return actual.indexOf(expected) !== -1;
153162
};
154163
}
155164

156165
predicateFn = function(item) {
157-
return deepCompare(item, expression, comparator);
166+
return deepCompare(item, expression, comparator, matchAgainstAnyProp);
158167
};
159168

160169
return predicateFn;
161170
}
162171

163-
function deepCompare(actual, expected, comparator, keyWasDollar) {
172+
function deepCompare(actual, expected, comparator, matchAgainstAnyProp) {
164173
var actualType = typeof actual;
165174
var expectedType = typeof expected;
166175

167176
if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
168-
return !deepCompare(actual, expected.substring(1), comparator);
177+
return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
169178
} else if (actualType === 'array') {
170179
// In case `actual` is an array, consider it a match
171-
// if any of it's items matches `expected`
180+
// if ANY of it's items matches `expected`
172181
return actual.some(function(item) {
173-
return deepCompare(item, expected, comparator);
182+
return deepCompare(item, expected, comparator, matchAgainstAnyProp);
174183
});
175184
}
176185

177186
switch (actualType) {
178187
case 'object':
179188
var key;
180-
if (keyWasDollar || (expectedType !== 'object')) {
189+
if (matchAgainstAnyProp) {
181190
for (key in actual) {
182191
if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator)) {
183192
return true;
184193
}
185194
}
186195
return false;
187-
} else {
196+
} else if (expectedType === 'object') {
188197
for (key in expected) {
189198
var expectedVal = expected[key];
190199
if (isFunction(expectedVal)) {
@@ -198,13 +207,13 @@ function deepCompare(actual, expected, comparator, keyWasDollar) {
198207
}
199208
}
200209
return true;
210+
} else {
211+
return comparator(actual, expected);
201212
}
202213
break;
214+
case 'function':
215+
return false;
203216
default:
204-
if (expectedType === 'object') {
205-
return false;
206-
}
207-
208217
return comparator(actual, expected);
209218
}
210219
}

test/ng/filter/filterSpec.js

+19-1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,17 @@ describe('Filter: filter', function() {
136136
});
137137

138138

139+
it('should respect the depth level of a "$" property', function() {
140+
var items = [{person: {name: 'Annet', email: 'annet@example.com'}},
141+
{person: {name: 'Billy', email: 'me@billy.com'}},
142+
{person: {name: 'Joan', email: {home: 'me@joan.com', work: 'joan@example.net'}}}];
143+
var expr = {person: {$: 'net'}};
144+
145+
expect(filter(items, expr).length).toBe(1);
146+
expect(filter(items, expr)).toEqual([items[0]]);
147+
});
148+
149+
139150
it('should respect the nesting level of "$"', function() {
140151
var items = [{supervisor: 'me', person: {name: 'Annet', email: 'annet@example.com'}},
141152
{supervisor: 'me', person: {name: 'Billy', email: 'me@billy.com'}},
@@ -314,6 +325,13 @@ describe('Filter: filter', function() {
314325

315326
describe('should support comparator', function() {
316327

328+
it('not consider `object === "[object Object]"` in non-strict comparison', function() {
329+
var items = [{test: {}}];
330+
var expr = '[object';
331+
expect(filter(items, expr).length).toBe(0);
332+
});
333+
334+
317335
it('as equality when true', function() {
318336
var items = ['misko', 'adam', 'adamson'];
319337
var expr = 'adam';
@@ -395,7 +413,7 @@ describe('Filter: filter', function() {
395413
return isString(actual) && isString(expected) && (actual.indexOf(expected) === 0);
396414
};
397415

398-
expr = {details: {email: 'admin@example.com', role: 'admn'}};
416+
expr = {details: {email: 'admin@example.com', role: 'min'}};
399417
expect(filter(items, expr, comp)).toEqual([]);
400418

401419
expr = {details: {email: 'admin@example.com', role: 'adm'}};

0 commit comments

Comments
 (0)