mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-01-12 22:45:52 +08:00
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:
committed by
Caitlin Potter
parent
5ced914cc8
commit
a75537d461
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'}};
|
||||
|
||||
Reference in New Issue
Block a user