fix(filterFilter): don't match primitive sub-expressions against any prop

Basically, implement the logic detailed in the 2nd point of
https://github.com/angular/angular.js/pull/9757#issuecomment-63544399
This commit is contained in:
Georgios Kalpakas
2014-11-24 16:32:06 +02:00
committed by Caitlin Potter
parent 5ced914cc8
commit a75537d461
2 changed files with 42 additions and 15 deletions

View File

@@ -120,6 +120,7 @@ function filterFilter() {
if (!isArray(array)) return array;
var predicateFn;
var matchAgainstAnyProp;
switch (typeof expression) {
case 'function':
@@ -127,9 +128,12 @@ function filterFilter() {
break;
case 'boolean':
case 'number':
case 'object':
case 'string':
predicateFn = createPredicateFn(expression, comparator);
matchAgainstAnyProp = true;
//jshint -W086
case 'object':
//jshint +W086
predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
break;
default:
return array;
@@ -140,13 +144,18 @@ function filterFilter() {
}
// Helper functions for `filterFilter`
function createPredicateFn(expression, comparator) {
function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
var predicateFn;
if (comparator === true) {
comparator = equals;
} else if (!isFunction(comparator)) {
comparator = function(actual, expected) {
if (isObject(actual) || isObject(expected)) {
// Prevent an object to be considered equal to a string like `'[object'`
return false;
}
actual = lowercase('' + actual);
expected = lowercase('' + expected);
return actual.indexOf(expected) !== -1;
@@ -154,37 +163,37 @@ function createPredicateFn(expression, comparator) {
}
predicateFn = function(item) {
return deepCompare(item, expression, comparator);
return deepCompare(item, expression, comparator, matchAgainstAnyProp);
};
return predicateFn;
}
function deepCompare(actual, expected, comparator, keyWasDollar) {
function deepCompare(actual, expected, comparator, matchAgainstAnyProp) {
var actualType = typeof actual;
var expectedType = typeof expected;
if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
return !deepCompare(actual, expected.substring(1), comparator);
return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
} else if (actualType === 'array') {
// In case `actual` is an array, consider it a match
// if any of it's items matches `expected`
// if ANY of it's items matches `expected`
return actual.some(function(item) {
return deepCompare(item, expected, comparator);
return deepCompare(item, expected, comparator, matchAgainstAnyProp);
});
}
switch (actualType) {
case 'object':
var key;
if (keyWasDollar || (expectedType !== 'object')) {
if (matchAgainstAnyProp) {
for (key in actual) {
if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator)) {
return true;
}
}
return false;
} else {
} else if (expectedType === 'object') {
for (key in expected) {
var expectedVal = expected[key];
if (isFunction(expectedVal)) {
@@ -198,13 +207,13 @@ function deepCompare(actual, expected, comparator, keyWasDollar) {
}
}
return true;
} else {
return comparator(actual, expected);
}
break;
case 'function':
return false;
default:
if (expectedType === 'object') {
return false;
}
return comparator(actual, expected);
}
}

View File

@@ -136,6 +136,17 @@ describe('Filter: filter', function() {
});
it('should respect the depth level of a "$" property', function() {
var items = [{person: {name: 'Annet', email: 'annet@example.com'}},
{person: {name: 'Billy', email: 'me@billy.com'}},
{person: {name: 'Joan', email: {home: 'me@joan.com', work: 'joan@example.net'}}}];
var expr = {person: {$: 'net'}};
expect(filter(items, expr).length).toBe(1);
expect(filter(items, expr)).toEqual([items[0]]);
});
it('should respect the nesting level of "$"', function() {
var items = [{supervisor: 'me', person: {name: 'Annet', email: 'annet@example.com'}},
{supervisor: 'me', person: {name: 'Billy', email: 'me@billy.com'}},
@@ -314,6 +325,13 @@ describe('Filter: filter', function() {
describe('should support comparator', function() {
it('not consider `object === "[object Object]"` in non-strict comparison', function() {
var items = [{test: {}}];
var expr = '[object';
expect(filter(items, expr).length).toBe(0);
});
it('as equality when true', function() {
var items = ['misko', 'adam', 'adamson'];
var expr = 'adam';
@@ -395,7 +413,7 @@ describe('Filter: filter', function() {
return isString(actual) && isString(expected) && (actual.indexOf(expected) === 0);
};
expr = {details: {email: 'admin@example.com', role: 'admn'}};
expr = {details: {email: 'admin@example.com', role: 'min'}};
expect(filter(items, expr, comp)).toEqual([]);
expr = {details: {email: 'admin@example.com', role: 'adm'}};