Unified
Split
Showing
with
44 additions
and 21 deletions.
- +22 −15 src/ng/filter/filter.js
- +22 −6 test/ng/filter/filterSpec.js
| @@ -14,19 +14,26 @@ | ||
| * | ||
| * Can be one of: | ||
| * | ||
| * - `string`: The string is evaluated as an expression and the resulting value is used for substring match against | ||
| * the contents of the `array`. All strings or objects with string properties in `array` that contain this string | ||
| * will be returned. The predicate can be negated by prefixing the string with `!`. | ||
| * - `string`: The string is used for matching against the contents of the `array`. All strings or | ||
| * objects with string properties in `array` that match this string will be returned. This also | ||
| * applies to nested object properties. | ||
| * The predicate can be negated by prefixing the string with `!`. | ||
| * | ||
| * - `Object`: A pattern object can be used to filter specific properties on objects contained | ||
| * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items | ||
| * which have property `name` containing "M" and property `phone` containing "1". A special | ||
| * property name `$` can be used (as in `{$:"text"}`) to accept a match against any | ||
| * property of the object. That's equivalent to the simple substring match with a `string` | ||
| * as described above. The predicate can be negated by prefixing the string with `!`. | ||
| * For Example `{name: "!M"}` predicate will return an array of items which have property `name` | ||
| * property of the object or its nested object properties. That's equivalent to the simple | ||
| * substring match with a `string` as described above. The predicate can be negated by prefixing | ||
| * the string with `!`. | ||
| * For example `{name: "!M"}` predicate will return an array of items which have property `name` | ||
| * not containing "M". | ||
| * | ||
| * Note that a named property will match properties on the same level only, while the special | ||
| * `$` property will match properties on the same level or deeper. E.g. an array item like | ||
| * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but | ||
| * **will** be matched by `{$: 'John'}`. | ||
| * | ||
| * - `function(value, index)`: A predicate function can be used to write arbitrary filters. The | ||
| * function is called for each element of `array`. The final result is an array of those | ||
| * elements that the predicate returned true for. | ||
| @@ -39,10 +46,10 @@ | ||
| * | ||
| * - `function(actual, expected)`: | ||
| * The function will be given the object value and the predicate value to compare and | ||
| * should return true if the item should be included in filtered result. | ||
| * should return true if both values should be considered equal. | ||
| * | ||
| * - `true`: A shorthand for `function(actual, expected) { return angular.equals(expected, actual)}`. | ||
| * this is essentially strict comparison of expected and actual. | ||
| * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`. | ||
| * This is essentially strict comparison of expected and actual. | ||
| * | ||
| * - `false|undefined`: A short hand for a function which will look for a substring match in case | ||
| * insensitive way. | ||
| @@ -173,7 +180,7 @@ function createPredicateFn(expression, comparator, matchAgainstAnyProp) { | ||
| return predicateFn; | ||
| } | ||
|
|
||
| function deepCompare(actual, expected, comparator, matchAgainstAnyProp) { | ||
| function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) { | ||
| var actualType = typeof actual; | ||
| var expectedType = typeof expected; | ||
|
|
||
| @@ -192,21 +199,21 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp) { | ||
| var key; | ||
| if (matchAgainstAnyProp) { | ||
| for (key in actual) { | ||
| if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator)) { | ||
| if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) { | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false); | ||
| } else if (expectedType === 'object') { | ||
| for (key in expected) { | ||
| var expectedVal = expected[key]; | ||
| if (isFunction(expectedVal)) { | ||
| continue; | ||
| } | ||
|
|
||
| var keyIsDollar = key === '$'; | ||
| var actualVal = keyIsDollar ? actual : actual[key]; | ||
| if (!deepCompare(actualVal, expectedVal, comparator, keyIsDollar)) { | ||
| var matchAnyProperty = key === '$'; | ||
| var actualVal = matchAnyProperty ? actual : actual[key]; | ||
| if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) { | ||
| return false; | ||
| } | ||
| } | ||
| @@ -163,14 +163,30 @@ 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'}}; | ||
| it('should match named properties only against named properties on the same level', function() { | ||
| var expr = {person: {name: 'John'}}; | ||
| var items = [{person: 'John'}, // No match (1 level higher) | ||
| {person: {name: 'John'}}, // Match (same level) | ||
| {person: {name: {first: 'John', last: 'Doe'}}}]; // No match (1 level deeper) | ||
|
|
||
| expect(filter(items, expr).length).toBe(1); | ||
| expect(filter(items, expr)).toEqual([items[0]]); | ||
| expect(filter(items, expr)).toEqual([items[1]]); | ||
| }); | ||
|
|
||
|
|
||
| it('should match any properties on same or deeper level for given "$" property', function() { | ||
| var items = [{level1: 'test', foo1: 'bar1'}, | ||
| {level1: {level2: 'test', foo2:'bar2'}, foo1: 'bar1'}, | ||
| {level1: {level2: {level3: 'test', foo3: 'bar3'}, foo2: 'bar2'}, foo1: 'bar1'}]; | ||
|
|
||
| expect(filter(items, {$: 'ES'}).length).toBe(3); | ||
| expect(filter(items, {$: 'ES'})).toEqual([items[0], items[1], items[2]]); | ||
|
|
||
| expect(filter(items, {level1: {$: 'ES'}}).length).toBe(2); | ||
| expect(filter(items, {level1: {$: 'ES'}})).toEqual([items[1], items[2]]); | ||
|
|
||
| expect(filter(items, {level1: {level2: {$: 'ES'}}}).length).toBe(1); | ||
| expect(filter(items, {level1: {level2: {$: 'ES'}}})).toEqual([items[2]]); | ||
| }); | ||
|
|
||
|
|
||