mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-01-12 17:12:34 +08:00
fix(filterFilter): make $ match properties on deeper levels as well
Closes #10401
This commit is contained in:
committed by
Lucas Galfaso
parent
cd77c089ba
commit
bd28c74c1d
@@ -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,11 +199,11 @@ 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];
|
||||
@@ -204,9 +211,9 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp) {
|
||||
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]]);
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user