diff --git a/src/ng/filter/filter.js b/src/ng/filter/filter.js index a739a1ca21c7..aeb4839d6b3e 100644 --- a/src/ng/filter/filter.js +++ b/src/ng/filter/filter.js @@ -120,7 +120,6 @@ function filterFilter() { if (!isArray(array)) return array; var predicateFn; - var matchAgainstAnyProp; switch (typeof expression) { case 'function': @@ -129,11 +128,8 @@ function filterFilter() { case 'boolean': case 'number': case 'string': - matchAgainstAnyProp = true; - //jshint -W086 case 'object': - //jshint +W086 - predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp); + predicateFn = createPredicateFn(expression, comparator); break; default: return array; @@ -144,7 +140,7 @@ function filterFilter() { } // Helper functions for `filterFilter` -function createPredicateFn(expression, comparator, matchAgainstAnyProp) { +function createPredicateFn(expression, comparator) { var predicateFn; if (comparator === true) { @@ -163,37 +159,32 @@ function createPredicateFn(expression, comparator, matchAgainstAnyProp) { } predicateFn = function(item) { - return deepCompare(item, expression, comparator, matchAgainstAnyProp); + return deepCompare(item, expression, comparator); }; return predicateFn; } -function deepCompare(actual, expected, comparator, matchAgainstAnyProp) { +function deepCompare(actual, expected, comparator) { var actualType = typeof actual; var expectedType = typeof expected; if ((expectedType === 'string') && (expected.charAt(0) === '!')) { - return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp); - } else if (actualType === 'array') { + return !deepCompare(actual, expected.substring(1), comparator); + } + + if (actualType === 'array') { // In case `actual` is an array, consider it a match // if ANY of it's items matches `expected` return actual.some(function(item) { - return deepCompare(item, expected, comparator, matchAgainstAnyProp); + return deepCompare(item, expected, comparator); }); } switch (actualType) { case 'object': var key; - if (matchAgainstAnyProp) { - for (key in actual) { - if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator)) { - return true; - } - } - return false; - } else if (expectedType === 'object') { + if (expectedType === 'object') { for (key in expected) { var expectedVal = expected[key]; if (isFunction(expectedVal)) { @@ -201,14 +192,25 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp) { } var keyIsDollar = key === '$'; - var actualVal = keyIsDollar ? actual : actual[key]; - if (!deepCompare(actualVal, expectedVal, comparator, keyIsDollar)) { - return false; + if (keyIsDollar) { + if (!matchAnyProp(actual, expectedVal, comparator)) { + return false; + } + } else { + var actualVal = actual[key]; + if (!deepCompare(actualVal, expectedVal, comparator)) { + return false; + } } } return true; } else { - return comparator(actual, expected); + for (key in actual) { + if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator)) { + return true; + } + } + return false; } break; case 'function': @@ -217,3 +219,16 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp) { return comparator(actual, expected); } } + +function matchAnyProp(actual, expected, comparator) { + if (typeof actual !== 'object') { + throw new Error('actual must be an object but was ' + typeof actual); + } + + for (var key in actual) { + if (deepCompare(actual[key], expected, comparator)) { + return true; + } + } + return false; +} diff --git a/test/ng/filter/filterSpec.js b/test/ng/filter/filterSpec.js index 7e14f5f567f4..864f1205e31c 100644 --- a/test/ng/filter/filterSpec.js +++ b/test/ng/filter/filterSpec.js @@ -28,6 +28,13 @@ describe('Filter: filter', function() { expect(filter(items, "I don't exist").length).toBe(0); }); + it('should filter deep object by string', 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'}}}]; + expect(filter(items, 'me@joan').length).toBe(1); + expect(filter(items, 'joan@example').length).toBe(1); + }); it('should not read $ properties', function() { expect(''.charAt(0)).toBe(''); // assumption @@ -136,14 +143,14 @@ describe('Filter: filter', function() { }); - it('should respect the depth level of a "$" property', function() { + it('should match the same level and deeper 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]]); + expect(filter(items, expr).length).toBe(2); + expect(filter(items, expr)).toEqual([items[0], items[2]]); });