aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMisko Hevery2011-11-03 20:53:33 -0700
committerMisko Hevery2011-11-14 20:31:13 -0800
commitdd9151e522220b438074e55c72f47ed2a8da9933 (patch)
tree9b184e61f9c771cac9dcaca6e897ea2b588324ff /src
parent3972d2a89bfcfe177b12bb225302fc2937a1dbab (diff)
downloadangular.js-dd9151e522220b438074e55c72f47ed2a8da9933.tar.bz2
refacter(filters): convert filter/limitTo/orderBy from type augmentation to filters
Diffstat (limited to 'src')
-rw-r--r--src/apis.js377
-rw-r--r--src/service/filter.js9
-rw-r--r--src/service/filter/filter.js164
-rw-r--r--src/service/filter/limitTo.js87
-rw-r--r--src/service/filter/orderBy.js137
5 files changed, 394 insertions, 380 deletions
diff --git a/src/apis.js b/src/apis.js
index db600013..269dc9a1 100644
--- a/src/apis.js
+++ b/src/apis.js
@@ -275,166 +275,6 @@ var angularArray = {
},
- /**
- * @ngdoc function
- * @name angular.Array.filter
- * @function
- *
- * @description
- * Selects a subset of items from `array` and returns it as a new array.
- *
- * Note: This function is used to augment the `Array` type in Angular expressions. See
- * {@link angular.Array} for more information about Angular arrays.
- *
- * @param {Array} array The source array.
- * @param {string|Object|function()} expression The predicate to be used for selecting items from
- * `array`.
- *
- * Can be one of:
- *
- * - `string`: Predicate that results in a substring match using the value of `expression`
- * string. 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 `!`.
- *
- * - `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.
- *
- * - `function`: 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.
- *
- * @example
- <doc:example>
- <doc:source>
- <div ng:init="friends = [{name:'John', phone:'555-1276'},
- {name:'Mary', phone:'800-BIG-MARY'},
- {name:'Mike', phone:'555-4321'},
- {name:'Adam', phone:'555-5678'},
- {name:'Julie', phone:'555-8765'}]"></div>
-
- Search: <input ng:model="searchText"/>
- <table id="searchTextResults">
- <tr><th>Name</th><th>Phone</th><tr>
- <tr ng:repeat="friend in friends.$filter(searchText)">
- <td>{{friend.name}}</td>
- <td>{{friend.phone}}</td>
- <tr>
- </table>
- <hr>
- Any: <input ng:model="search.$"/> <br>
- Name only <input ng:model="search.name"/><br>
- Phone only <input ng:model="search.phone"/><br>
- <table id="searchObjResults">
- <tr><th>Name</th><th>Phone</th><tr>
- <tr ng:repeat="friend in friends.$filter(search)">
- <td>{{friend.name}}</td>
- <td>{{friend.phone}}</td>
- <tr>
- </table>
- </doc:source>
- <doc:scenario>
- it('should search across all fields when filtering with a string', function() {
- input('searchText').enter('m');
- expect(repeater('#searchTextResults tr', 'friend in friends').column('name')).
- toEqual(['Mary', 'Mike', 'Adam']);
-
- input('searchText').enter('76');
- expect(repeater('#searchTextResults tr', 'friend in friends').column('name')).
- toEqual(['John', 'Julie']);
- });
-
- it('should search in specific fields when filtering with a predicate object', function() {
- input('search.$').enter('i');
- expect(repeater('#searchObjResults tr', 'friend in friends').column('name')).
- toEqual(['Mary', 'Mike', 'Julie']);
- });
- </doc:scenario>
- </doc:example>
- */
- 'filter':function(array, expression) {
- var predicates = [];
- predicates.check = function(value) {
- for (var j = 0; j < predicates.length; j++) {
- if(!predicates[j](value)) {
- return false;
- }
- }
- return true;
- };
- var search = function(obj, text){
- if (text.charAt(0) === '!') {
- return !search(obj, text.substr(1));
- }
- switch (typeof obj) {
- case "boolean":
- case "number":
- case "string":
- return ('' + obj).toLowerCase().indexOf(text) > -1;
- case "object":
- for ( var objKey in obj) {
- if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
- return true;
- }
- }
- return false;
- case "array":
- for ( var i = 0; i < obj.length; i++) {
- if (search(obj[i], text)) {
- return true;
- }
- }
- return false;
- default:
- return false;
- }
- };
- switch (typeof expression) {
- case "boolean":
- case "number":
- case "string":
- expression = {$:expression};
- case "object":
- for (var key in expression) {
- if (key == '$') {
- (function() {
- var text = (''+expression[key]).toLowerCase();
- if (!text) return;
- predicates.push(function(value) {
- return search(value, text);
- });
- })();
- } else {
- (function() {
- var path = key;
- var text = (''+expression[key]).toLowerCase();
- if (!text) return;
- predicates.push(function(value) {
- return search(getter(value, path), text);
- });
- })();
- }
- }
- break;
- case 'function':
- predicates.push(expression);
- break;
- default:
- return array;
- }
- var filtered = [];
- for ( var j = 0; j < array.length; j++) {
- var value = array[j];
- if (predicates.check(value)) {
- filtered.push(value);
- }
- }
- return filtered;
- },
-
/**
* @ngdoc function
@@ -577,223 +417,6 @@ var angularArray = {
}
});
return count;
- },
-
-
- /**
- * @ngdoc function
- * @name angular.Array.orderBy
- * @function
- *
- * @description
- * Orders a specified `array` by the `expression` predicate.
- *
- * Note: this function is used to augment the `Array` type in Angular expressions. See
- * {@link angular.Array} for more informaton about Angular arrays.
- *
- * @param {Array} array The array to sort.
- * @param {function(*)|string|Array.<(function(*)|string)>} expression A predicate to be
- * used by the comparator to determine the order of elements.
- *
- * Can be one of:
- *
- * - `function`: Getter function. The result of this function will be sorted using the
- * `<`, `=`, `>` operator.
- * - `string`: An Angular expression which evaluates to an object to order by, such as 'name'
- * to sort by a property called 'name'. Optionally prefixed with `+` or `-` to control
- * ascending or descending sort order (for example, +name or -name).
- * - `Array`: An array of function or string predicates. The first predicate in the array
- * is used for sorting, but when two items are equivalent, the next predicate is used.
- *
- * @param {boolean=} reverse Reverse the order the array.
- * @returns {Array} Sorted copy of the source array.
- *
- * @example
- <doc:example>
- <doc:source>
- <script>
- function Ctrl() {
- this.friends =
- [{name:'John', phone:'555-1212', age:10},
- {name:'Mary', phone:'555-9876', age:19},
- {name:'Mike', phone:'555-4321', age:21},
- {name:'Adam', phone:'555-5678', age:35},
- {name:'Julie', phone:'555-8765', age:29}]
- this.predicate = '-age';
- }
- </script>
- <div ng:controller="Ctrl">
- <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
- <hr/>
- [ <a href="" ng:click="predicate=''">unsorted</a> ]
- <table class="friend">
- <tr>
- <th><a href="" ng:click="predicate = 'name'; reverse=false">Name</a>
- (<a href ng:click="predicate = '-name'; reverse=false">^</a>)</th>
- <th><a href="" ng:click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
- <th><a href="" ng:click="predicate = 'age'; reverse=!reverse">Age</a></th>
- <tr>
- <tr ng:repeat="friend in friends.$orderBy(predicate, reverse)">
- <td>{{friend.name}}</td>
- <td>{{friend.phone}}</td>
- <td>{{friend.age}}</td>
- <tr>
- </table>
- </div>
- </doc:source>
- <doc:scenario>
- it('should be reverse ordered by aged', function() {
- expect(binding('predicate')).toBe('Sorting predicate = -age; reverse = ');
- expect(repeater('table.friend', 'friend in friends').column('friend.age')).
- toEqual(['35', '29', '21', '19', '10']);
- expect(repeater('table.friend', 'friend in friends').column('friend.name')).
- toEqual(['Adam', 'Julie', 'Mike', 'Mary', 'John']);
- });
-
- it('should reorder the table when user selects different predicate', function() {
- element('.doc-example-live a:contains("Name")').click();
- expect(repeater('table.friend', 'friend in friends').column('friend.name')).
- toEqual(['Adam', 'John', 'Julie', 'Mary', 'Mike']);
- expect(repeater('table.friend', 'friend in friends').column('friend.age')).
- toEqual(['35', '10', '29', '19', '21']);
-
- element('.doc-example-live a:contains("Phone")').click();
- expect(repeater('table.friend', 'friend in friends').column('friend.phone')).
- toEqual(['555-9876', '555-8765', '555-5678', '555-4321', '555-1212']);
- expect(repeater('table.friend', 'friend in friends').column('friend.name')).
- toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']);
- });
- </doc:scenario>
- </doc:example>
- */
- 'orderBy':function(array, sortPredicate, reverseOrder) {
- if (!sortPredicate) return array;
- sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
- sortPredicate = map(sortPredicate, function(predicate){
- var descending = false, get = predicate || identity;
- if (isString(predicate)) {
- if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
- descending = predicate.charAt(0) == '-';
- predicate = predicate.substring(1);
- }
- get = expressionCompile(predicate);
- }
- return reverseComparator(function(a,b){
- return compare(get(a),get(b));
- }, descending);
- });
- var arrayCopy = [];
- for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); }
- return arrayCopy.sort(reverseComparator(comparator, reverseOrder));
-
- function comparator(o1, o2){
- for ( var i = 0; i < sortPredicate.length; i++) {
- var comp = sortPredicate[i](o1, o2);
- if (comp !== 0) return comp;
- }
- return 0;
- }
- function reverseComparator(comp, descending) {
- return toBoolean(descending)
- ? function(a,b){return comp(b,a);}
- : comp;
- }
- function compare(v1, v2){
- var t1 = typeof v1;
- var t2 = typeof v2;
- if (t1 == t2) {
- if (t1 == "string") v1 = v1.toLowerCase();
- if (t1 == "string") v2 = v2.toLowerCase();
- if (v1 === v2) return 0;
- return v1 < v2 ? -1 : 1;
- } else {
- return t1 < t2 ? -1 : 1;
- }
- }
- },
-
-
- /**
- * @ngdoc function
- * @name angular.Array.limitTo
- * @function
- *
- * @description
- * Creates a new array containing only a specified number of elements in an array. The elements
- * are taken from either the beginning or the end of the source array, as specified by the
- * value and sign (positive or negative) of `limit`.
- *
- * Note: This function is used to augment the `Array` type in Angular expressions. See
- * {@link angular.Array} for more information about Angular arrays.
- *
- * @param {Array} array Source array to be limited.
- * @param {string|Number} limit The length of the returned array. If the `limit` number is
- * positive, `limit` number of items from the beginning of the source array are copied.
- * If the number is negative, `limit` number of items from the end of the source array are
- * copied. The `limit` will be trimmed if it exceeds `array.length`
- * @returns {Array} A new sub-array of length `limit` or less if input array had less than `limit`
- * elements.
- *
- * @example
- <doc:example>
- <doc:source>
- <script>
- function Ctrl() {
- this.numbers = [1,2,3,4,5,6,7,8,9];
- this.limit = 3;
- }
- </script>
- <div ng:controller="Ctrl">
- Limit {{numbers}} to: <input type="integer" ng:model="limit"/>
- <p>Output: {{ numbers.$limitTo(limit) | json }}</p>
- </div>
- </doc:source>
- <doc:scenario>
- it('should limit the numer array to first three items', function() {
- expect(element('.doc-example-live input[ng\\:model=limit]').val()).toBe('3');
- expect(binding('numbers.$limitTo(limit) | json')).toEqual('[1,2,3]');
- });
-
- it('should update the output when -3 is entered', function() {
- input('limit').enter(-3);
- expect(binding('numbers.$limitTo(limit) | json')).toEqual('[7,8,9]');
- });
-
- it('should not exceed the maximum size of input array', function() {
- input('limit').enter(100);
- expect(binding('numbers.$limitTo(limit) | json')).toEqual('[1,2,3,4,5,6,7,8,9]');
- });
- </doc:scenario>
- </doc:example>
- */
- limitTo: function(array, limit) {
- limit = parseInt(limit, 10);
- var out = [],
- i, n;
-
- // check that array is iterable
- if (!array || !(array instanceof Array))
- return out;
-
- // if abs(limit) exceeds maximum length, trim it
- if (limit > array.length)
- limit = array.length;
- else if (limit < -array.length)
- limit = -array.length;
-
- if (limit > 0) {
- i = 0;
- n = limit;
- } else {
- i = array.length + limit;
- n = array.length;
- }
-
- for (; i<n; i++) {
- out.push(array[i]);
- }
-
- return out;
}
};
diff --git a/src/service/filter.js b/src/service/filter.js
index 7b85c23d..9d6a4001 100644
--- a/src/service/filter.js
+++ b/src/service/filter.js
@@ -17,11 +17,14 @@ function $FilterProvider($provide) {
////////////////////////////////////////
$provide.filter('currency', currencyFilter);
- $provide.filter('number', numberFilter);
$provide.filter('date', dateFilter);
+ $provide.filter('filter', filterFilter);
+ $provide.filter('html', htmlFilter);
$provide.filter('json', jsonFilter);
+ $provide.filter('limitTo', limitToFilter);
+ $provide.filter('linky', linkyFilter);
$provide.filter('lowercase', lowercaseFilter);
+ $provide.filter('number', numberFilter);
+ $provide.filter('orderBy', orderByFilter);
$provide.filter('uppercase', uppercaseFilter);
- $provide.filter('html', htmlFilter);
- $provide.filter('linky', linkyFilter);
}
diff --git a/src/service/filter/filter.js b/src/service/filter/filter.js
new file mode 100644
index 00000000..0a0f5706
--- /dev/null
+++ b/src/service/filter/filter.js
@@ -0,0 +1,164 @@
+'use strict';
+
+/**
+ * @ngdoc function
+ * @name angular.Array.filter
+ * @function
+ *
+ * @description
+ * Selects a subset of items from `array` and returns it as a new array.
+ *
+ * Note: This function is used to augment the `Array` type in Angular expressions. See
+ * {@link angular.Array} for more information about Angular arrays.
+ *
+ * @param {Array} array The source array.
+ * @param {string|Object|function()} expression The predicate to be used for selecting items from
+ * `array`.
+ *
+ * Can be one of:
+ *
+ * - `string`: Predicate that results in a substring match using the value of `expression`
+ * string. 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 `!`.
+ *
+ * - `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.
+ *
+ * - `function`: 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.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <div ng:init="friends = [{name:'John', phone:'555-1276'},
+ {name:'Mary', phone:'800-BIG-MARY'},
+ {name:'Mike', phone:'555-4321'},
+ {name:'Adam', phone:'555-5678'},
+ {name:'Julie', phone:'555-8765'}]"></div>
+
+ Search: <input ng:model="searchText"/>
+ <table id="searchTextResults">
+ <tr><th>Name</th><th>Phone</th><tr>
+ <tr ng:repeat="friend in friends.$filter(searchText)">
+ <td>{{friend.name}}</td>
+ <td>{{friend.phone}}</td>
+ <tr>
+ </table>
+ <hr>
+ Any: <input ng:model="search.$"/> <br>
+ Name only <input ng:model="search.name"/><br>
+ Phone only <input ng:model="search.phone"/><br>
+ <table id="searchObjResults">
+ <tr><th>Name</th><th>Phone</th><tr>
+ <tr ng:repeat="friend in friends.$filter(search)">
+ <td>{{friend.name}}</td>
+ <td>{{friend.phone}}</td>
+ <tr>
+ </table>
+ </doc:source>
+ <doc:scenario>
+ it('should search across all fields when filtering with a string', function() {
+ input('searchText').enter('m');
+ expect(repeater('#searchTextResults tr', 'friend in friends').column('name')).
+ toEqual(['Mary', 'Mike', 'Adam']);
+
+ input('searchText').enter('76');
+ expect(repeater('#searchTextResults tr', 'friend in friends').column('name')).
+ toEqual(['John', 'Julie']);
+ });
+
+ it('should search in specific fields when filtering with a predicate object', function() {
+ input('search.$').enter('i');
+ expect(repeater('#searchObjResults tr', 'friend in friends').column('name')).
+ toEqual(['Mary', 'Mike', 'Julie']);
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+function filterFilter() {
+ return function(array, expression) {
+ if (!(array instanceof Array)) return array;
+ var predicates = [];
+ predicates.check = function(value) {
+ for (var j = 0; j < predicates.length; j++) {
+ if(!predicates[j](value)) {
+ return false;
+ }
+ }
+ return true;
+ };
+ var search = function(obj, text){
+ if (text.charAt(0) === '!') {
+ return !search(obj, text.substr(1));
+ }
+ switch (typeof obj) {
+ case "boolean":
+ case "number":
+ case "string":
+ return ('' + obj).toLowerCase().indexOf(text) > -1;
+ case "object":
+ for ( var objKey in obj) {
+ if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
+ return true;
+ }
+ }
+ return false;
+ case "array":
+ for ( var i = 0; i < obj.length; i++) {
+ if (search(obj[i], text)) {
+ return true;
+ }
+ }
+ return false;
+ default:
+ return false;
+ }
+ };
+ switch (typeof expression) {
+ case "boolean":
+ case "number":
+ case "string":
+ expression = {$:expression};
+ case "object":
+ for (var key in expression) {
+ if (key == '$') {
+ (function() {
+ var text = (''+expression[key]).toLowerCase();
+ if (!text) return;
+ predicates.push(function(value) {
+ return search(value, text);
+ });
+ })();
+ } else {
+ (function() {
+ var path = key;
+ var text = (''+expression[key]).toLowerCase();
+ if (!text) return;
+ predicates.push(function(value) {
+ return search(getter(value, path), text);
+ });
+ })();
+ }
+ }
+ break;
+ case 'function':
+ predicates.push(expression);
+ break;
+ default:
+ return array;
+ }
+ var filtered = [];
+ for ( var j = 0; j < array.length; j++) {
+ var value = array[j];
+ if (predicates.check(value)) {
+ filtered.push(value);
+ }
+ }
+ return filtered;
+ }
+}
diff --git a/src/service/filter/limitTo.js b/src/service/filter/limitTo.js
new file mode 100644
index 00000000..9bb5cf4d
--- /dev/null
+++ b/src/service/filter/limitTo.js
@@ -0,0 +1,87 @@
+'use strict';
+
+/**
+ * @ngdoc function
+ * @name angular.Array.limitTo
+ * @function
+ *
+ * @description
+ * Creates a new array containing only a specified number of elements in an array. The elements
+ * are taken from either the beginning or the end of the source array, as specified by the
+ * value and sign (positive or negative) of `limit`.
+ *
+ * Note: This function is used to augment the `Array` type in Angular expressions. See
+ * {@link angular.Array} for more information about Angular arrays.
+ *
+ * @param {Array} array Source array to be limited.
+ * @param {string|Number} limit The length of the returned array. If the `limit` number is
+ * positive, `limit` number of items from the beginning of the source array are copied.
+ * If the number is negative, `limit` number of items from the end of the source array are
+ * copied. The `limit` will be trimmed if it exceeds `array.length`
+ * @returns {Array} A new sub-array of length `limit` or less if input array had less than `limit`
+ * elements.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <script>
+ function Ctrl() {
+ this.numbers = [1,2,3,4,5,6,7,8,9];
+ this.limit = 3;
+ }
+ </script>
+ <div ng:controller="Ctrl">
+ Limit {{numbers}} to: <input type="integer" ng:model="limit"/>
+ <p>Output: {{ numbers.$limitTo(limit) | json }}</p>
+ </div>
+ </doc:source>
+ <doc:scenario>
+ it('should limit the numer array to first three items', function() {
+ expect(element('.doc-example-live input[ng\\:model=limit]').val()).toBe('3');
+ expect(binding('numbers.$limitTo(limit) | json')).toEqual('[1,2,3]');
+ });
+
+ it('should update the output when -3 is entered', function() {
+ input('limit').enter(-3);
+ expect(binding('numbers.$limitTo(limit) | json')).toEqual('[7,8,9]');
+ });
+
+ it('should not exceed the maximum size of input array', function() {
+ input('limit').enter(100);
+ expect(binding('numbers.$limitTo(limit) | json')).toEqual('[1,2,3,4,5,6,7,8,9]');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+function limitToFilter(){
+ return function(array, limit) {
+ if (!(array instanceof Array)) return array;
+ limit = parseInt(limit, 10);
+ var out = [],
+ i, n;
+
+ // check that array is iterable
+ if (!array || !(array instanceof Array))
+ return out;
+
+ // if abs(limit) exceeds maximum length, trim it
+ if (limit > array.length)
+ limit = array.length;
+ else if (limit < -array.length)
+ limit = -array.length;
+
+ if (limit > 0) {
+ i = 0;
+ n = limit;
+ } else {
+ i = array.length + limit;
+ n = array.length;
+ }
+
+ for (; i<n; i++) {
+ out.push(array[i]);
+ }
+
+ return out;
+ }
+}
diff --git a/src/service/filter/orderBy.js b/src/service/filter/orderBy.js
new file mode 100644
index 00000000..07c69af3
--- /dev/null
+++ b/src/service/filter/orderBy.js
@@ -0,0 +1,137 @@
+'use strict';
+
+/**
+ * @ngdoc function
+ * @name angular.Array.orderBy
+ * @function
+ *
+ * @description
+ * Orders a specified `array` by the `expression` predicate.
+ *
+ * Note: this function is used to augment the `Array` type in Angular expressions. See
+ * {@link angular.Array} for more informaton about Angular arrays.
+ *
+ * @param {Array} array The array to sort.
+ * @param {function(*)|string|Array.<(function(*)|string)>} expression A predicate to be
+ * used by the comparator to determine the order of elements.
+ *
+ * Can be one of:
+ *
+ * - `function`: Getter function. The result of this function will be sorted using the
+ * `<`, `=`, `>` operator.
+ * - `string`: An Angular expression which evaluates to an object to order by, such as 'name'
+ * to sort by a property called 'name'. Optionally prefixed with `+` or `-` to control
+ * ascending or descending sort order (for example, +name or -name).
+ * - `Array`: An array of function or string predicates. The first predicate in the array
+ * is used for sorting, but when two items are equivalent, the next predicate is used.
+ *
+ * @param {boolean=} reverse Reverse the order the array.
+ * @returns {Array} Sorted copy of the source array.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <script>
+ function Ctrl() {
+ this.friends =
+ [{name:'John', phone:'555-1212', age:10},
+ {name:'Mary', phone:'555-9876', age:19},
+ {name:'Mike', phone:'555-4321', age:21},
+ {name:'Adam', phone:'555-5678', age:35},
+ {name:'Julie', phone:'555-8765', age:29}]
+ this.predicate = '-age';
+ }
+ </script>
+ <div ng:controller="Ctrl">
+ <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
+ <hr/>
+ [ <a href="" ng:click="predicate=''">unsorted</a> ]
+ <table class="friend">
+ <tr>
+ <th><a href="" ng:click="predicate = 'name'; reverse=false">Name</a>
+ (<a href ng:click="predicate = '-name'; reverse=false">^</a>)</th>
+ <th><a href="" ng:click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
+ <th><a href="" ng:click="predicate = 'age'; reverse=!reverse">Age</a></th>
+ <tr>
+ <tr ng:repeat="friend in friends.$orderBy(predicate, reverse)">
+ <td>{{friend.name}}</td>
+ <td>{{friend.phone}}</td>
+ <td>{{friend.age}}</td>
+ <tr>
+ </table>
+ </div>
+ </doc:source>
+ <doc:scenario>
+ it('should be reverse ordered by aged', function() {
+ expect(binding('predicate')).toBe('Sorting predicate = -age; reverse = ');
+ expect(repeater('table.friend', 'friend in friends').column('friend.age')).
+ toEqual(['35', '29', '21', '19', '10']);
+ expect(repeater('table.friend', 'friend in friends').column('friend.name')).
+ toEqual(['Adam', 'Julie', 'Mike', 'Mary', 'John']);
+ });
+
+ it('should reorder the table when user selects different predicate', function() {
+ element('.doc-example-live a:contains("Name")').click();
+ expect(repeater('table.friend', 'friend in friends').column('friend.name')).
+ toEqual(['Adam', 'John', 'Julie', 'Mary', 'Mike']);
+ expect(repeater('table.friend', 'friend in friends').column('friend.age')).
+ toEqual(['35', '10', '29', '19', '21']);
+
+ element('.doc-example-live a:contains("Phone")').click();
+ expect(repeater('table.friend', 'friend in friends').column('friend.phone')).
+ toEqual(['555-9876', '555-8765', '555-5678', '555-4321', '555-1212']);
+ expect(repeater('table.friend', 'friend in friends').column('friend.name')).
+ toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']);
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+orderByFilter.$inject = ['$parse'];
+function orderByFilter($parse){
+ return function(array, sortPredicate, reverseOrder) {
+ if (!(array instanceof Array)) return array;
+ if (!sortPredicate) return array;
+ sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
+ sortPredicate = map(sortPredicate, function(predicate){
+ var descending = false, get = predicate || identity;
+ if (isString(predicate)) {
+ if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
+ descending = predicate.charAt(0) == '-';
+ predicate = predicate.substring(1);
+ }
+ get = $parse(predicate);
+ }
+ return reverseComparator(function(a,b){
+ return compare(get(a),get(b));
+ }, descending);
+ });
+ var arrayCopy = [];
+ for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); }
+ return arrayCopy.sort(reverseComparator(comparator, reverseOrder));
+
+ function comparator(o1, o2){
+ for ( var i = 0; i < sortPredicate.length; i++) {
+ var comp = sortPredicate[i](o1, o2);
+ if (comp !== 0) return comp;
+ }
+ return 0;
+ }
+ function reverseComparator(comp, descending) {
+ return toBoolean(descending)
+ ? function(a,b){return comp(b,a);}
+ : comp;
+ }
+ function compare(v1, v2){
+ var t1 = typeof v1;
+ var t2 = typeof v2;
+ if (t1 == t2) {
+ if (t1 == "string") v1 = v1.toLowerCase();
+ if (t1 == "string") v2 = v2.toLowerCase();
+ if (v1 === v2) return 0;
+ return v1 < v2 ? -1 : 1;
+ } else {
+ return t1 < t2 ? -1 : 1;
+ }
+ }
+ }
+}