X Tutup
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions modules/angular2/pipes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export {JsonPipe} from './src/change_detection/pipes/json_pipe';
export {DatePipe} from './src/change_detection/pipes/date_pipe';
export {DecimalPipe, PercentPipe, CurrencyPipe} from './src/change_detection/pipes/number_pipe';
export {LimitToPipe} from './src/change_detection/pipes/limit_to_pipe';
export {OrderByPipe} from './src/change_detection/pipes/order_by_pipe';
11 changes: 10 additions & 1 deletion modules/angular2/src/change_detection/change_detection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {JsonPipe} from './pipes/json_pipe';
import {LimitToPipeFactory} from './pipes/limit_to_pipe';
import {DatePipe} from './pipes/date_pipe';
import {DecimalPipe, PercentPipe, CurrencyPipe} from './pipes/number_pipe';
import {OrderByPipeFactory} from './pipes/order_by_pipe';
import {NullPipeFactory} from './pipes/null_pipe';
import {ChangeDetection, ProtoChangeDetector, ChangeDetectorDefinition} from './interfaces';
import {Inject, Injectable, OpaqueToken, Optional} from 'angular2/di';
Expand Down Expand Up @@ -129,6 +130,13 @@ export const currency: List<PipeFactory> =
export const date: List<PipeFactory> =
CONST_EXPR([CONST_EXPR(new DatePipe()), CONST_EXPR(new NullPipeFactory())]);

/**
* Sorts an iterable.
*
* @exportedAs angular2/pipes
*/
export const orderBy: List<PipeFactory> =
CONST_EXPR([CONST_EXPR(new OrderByPipeFactory()), CONST_EXPR(new NullPipeFactory())]);

export const defaultPipes: Pipes = CONST_EXPR(new Pipes({
"async": async,
Expand All @@ -139,7 +147,8 @@ export const defaultPipes: Pipes = CONST_EXPR(new Pipes({
"number": decimal,
"percent": percent,
"currency": currency,
"date": date
"date": date,
"orderBy": orderBy
}));

export const defaultIterableDiffers = CONST_EXPR(new IterableDiffers(iterableDiff));
Expand Down
219 changes: 219 additions & 0 deletions modules/angular2/src/change_detection/pipes/order_by_pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
import {
StringWrapper,
isBlank,
isString,
isArray,
CONST,
FunctionWrapper,
compare,
isDart
} from 'angular2/src/facade/lang';
import {isListLikeIterable, ListWrapper, CompareFn} from 'angular2/src/facade/collection';
import {Pipe, BasePipe, PipeFactory} from './pipe';
import {Parser} from 'angular2/src/change_detection/parser/parser';
import {Lexer} from 'angular2/src/change_detection/parser/lexer';
import {ChangeDetectorRef} from '../change_detector_ref';

function _nop(e) {
return e;
}
var _parser = new Parser(new Lexer());

/**
* (**Javascript only**) Orders the the elements of an [Iterable] using a predicate.
*
* # Usage
*
* expression | orderBy:predicate[:descending]
*
* The input to orderBy must be an [Iterable] object. The predicate may be specified as:
*
* - **a string**: a string containing an expression, such as "user.lastName", used to order the
* list.
* - **a custom function**: a function that will be called to transform the element
* before a sort.
* - **a List**: it may consist of either strings or functions. A list expression
* indicates a list of fallback expressions to use when a comparison results in the items being
* equal.
*
* If the expression is explicitly empty(`orderBy:''`), the elements are sorted in
* ascending order, using the default comparator, `+`.
*
* A string expression in the predicate can be prefixed to indicate sort order:
*
* - `+`: sort the elements in ascending order. This is the default.
* - `-`: sort the elements in descending order.
*
* Alternately, by appending `true`, you can set "descending order" to true, which has the same
* effect as the `-` prefix.
*
* # Examples
*
* ## Example 1: Simple array and single/empty expression.
*
* Assume that you have an array on scope called `colors` and that it has a list
* of these strings – `['red', 'blue', 'green']`. You might sort these in
* ascending order this way:
*
* Colors: <ul>
* <li *ng-for="var color of colors | orderBy:''">{{color}}</li>
* </ul>
*
* That would result in:
*
* <ul>
* <li>blue</li>
* <li>green</li>
* <li>red</li>
* <ul>
*
* The empty string expression, `''`, here signifies sorting in ascending order
* using the default comparator. Using `'+'` would also work, as the `+` prefix
* is implied.
*
* To sort in descending order, you would use the `'-'` prefix.
*
* Colors: <ul>
* <li *ng-for="var color of colors | orderBy:'-'">{{color}}</li>
* </ul>
*
* For this simple example, you could have also provided `true` as the addition
* optional parameter which requests a reverse order sort to get the same
* result.
*
* <!-- Same result (descending order) as previous snippet. -->
* Colors: <ul>
* <li *ng-for="var color of colors | orderBy:'':true">{{color}}</li>
* </ul>
*
* ## Example 2: Complex objects, single expression.
*
* You may provide a more complex expression to sort non-primitive values or
* if you want to sort on a decorated/transformed value.
*
* e.g. Support you have a list `users` that looks like this:
*
* authors = [
* {firstName: 'Emily', lastName: 'Bronte'},
* {firstName: 'Mark', lastName: 'Twain'},
* {firstName: 'Jeffrey', lastName: 'Archer'},
* {firstName: 'Isaac', lastName: 'Asimov'},
* {firstName: 'Oscar', lastName: 'Wilde'},
* ];
*
* If you want to list the authors sorted by `lastName`, you would use
*
* <li *ng-for="var author of authors | orderBy:'lastName'">
* {{author.lastName}}, {{author.firstName}}
* </li>
*
* The string expression, `'lastName'`, indicates that the sort should be on the
* `lastName` property of each item.
*
* Using the lesson from the previous example, you may sort in reverse order of
* lastName using either of the two methods.
*
* <!-- reverse order of last names -->
* <li *ng-for="var author of authors | orderBy:'-lastName'">
* <!-- also does the same thing -->
* <li *ng-for="var author of authors | orderBy:'lastName':true">
*
* Note that, while we only discussed string expressions, such as `"lastName"`
* or the empty string, you can also directly provide a custom function that
* will be called to transform the element before a sort.
*
* <li *ng-for="var author of authors | orderBy:getAuthorId">
*
* In the previous snippet, `getAuthorId` would evaluate to a callable when
* evaluated. That callable is called once for each element in the list
* (i.e. each author object) and the sort order is determined by the sort order
* of the value mapped by the callable.
*
* ## Example 3: List expressions
*
* Both a string expression and the callable expression are simple versions of
* the more general list expression. You may pass a list as the orderBy
* expression and this list may consist of either of the string or functions
* you saw in the previous examples. A list expression indicates a list of
* fallback expressions to use when a comparision results in the items being equal.
*
* For example, one might want to sort the authors list, first by last name and
* then by first name when the last names are equal. You would do that like
* this:
*
* <li *ng-for="var author of authors | orderBy:['lastName', 'firstName']">
*
* The items in such a list may either be string expressions or functions.
*/
export class OrderByPipe extends BasePipe {
_lastCompareFn = null;
_lastExpression = null;

static supportsObj(obj: any): boolean { return isListLikeIterable(obj); }

supports(obj: any): boolean { return OrderByPipe.supportsObj(obj); }

static _createMapper(expression: string) {
var ast = _parser.parseBinding(expression, '');
return function(e) { return ast.eval(e, null); };
}

static _buildCompareFn(expressions: any[], descending: boolean = false): CompareFn<any> {
var numExpressions: int = expressions.length;
var mappers: Function[] = ListWrapper.createFixedSize(numExpressions);
var cmpSign: int[] = ListWrapper.createFixedSize(numExpressions);
for (var i = 0; i < numExpressions; i++) {
var expression = expressions[i];
cmpSign[i] = descending ? -1 : 1;
if (isString(expression)) {
var strExp: string = expression;
if (StringWrapper.startsWith(strExp, '-') || StringWrapper.startsWith(strExp, '+')) {
if (StringWrapper.startsWith(strExp, '-')) cmpSign[i] *= -1;
strExp = StringWrapper.substring(strExp, 1);
}
if (strExp == '') {
mappers[i] = _nop;
} else {
mappers[i] = OrderByPipe._createMapper(strExp);
}
} else {
mappers[i] = expression;
}
}
return function(a, b) {
for (var i = 0; i < numExpressions; i++) {
var result = compare(mappers[i](a), mappers[i](b));
if (result != 0) return result * cmpSign[i];
}
return 0;
};
}

transform(value: any, args: any[]): any[] {
if (isBlank(args)) return value;
var expression = args.length > 0 ? args[0] : null;
var descending: boolean = args.length > 1 ? args[1] : false;

value = ListWrapper.from(value);
if (isBlank(expression)) return value;
var compareFn: CompareFn<any>;
if (!isArray(expression)) {
if (expression !== this._lastExpression) {
this._lastExpression = expression;
this._lastCompareFn = OrderByPipe._buildCompareFn([expression], descending);
}
compareFn = this._lastCompareFn;
} else {
compareFn = OrderByPipe._buildCompareFn(expression, descending);
}
ListWrapper.sort(value, compareFn);
return value;
}
}

@CONST()
export class OrderByPipeFactory implements PipeFactory {
supports(obj: any): boolean { return !isDart && OrderByPipe.supportsObj(obj); }

create(cdRef: ChangeDetectorRef): Pipe { return new OrderByPipe(); }
}
4 changes: 3 additions & 1 deletion modules/angular2/src/facade/collection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,14 @@ class StringMapWrapper {
}

typedef bool Predicate<T>(T item);
typedef int CompareFn<T>(T me, T other);

class ListWrapper {
static List clone(Iterable l) => new List.from(l);
static List createFixedSize(int size) => new List(size);
static List createGrowableSize(int size) =>
new List.generate(size, (_) => null, growable: true);
static List from(iterable) => new List.from(iterable);
static get(List m, int k) => m[k];
static void set(List m, int k, v) {
m[k] = v;
Expand Down Expand Up @@ -172,7 +174,7 @@ class ListWrapper {
l.removeRange(from, to);
return sub;
}
static void sort(List l, [compareFn(a, b) = null]) {
static void sort(List l, [CompareFn compareFn = null]) {
if (compareFn == null) {
l.sort();
} else {
Expand Down
7 changes: 6 additions & 1 deletion modules/angular2/src/facade/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,17 @@ export class StringMapWrapper {
}

export interface Predicate<T> { (value: T, index?: number, array?: T[]): boolean; }
export interface CompareFn<T> { (me: T, other: T): int; }

export class ListWrapper {
// JS has no way to express a staticly fixed size list, but dart does so we
// keep both methods.
static createFixedSize(size: number): List<any> { return new List(size); }
static createGrowableSize(size: number): List<any> { return new List(size); }
static from(iterable: any): List<any> {
// Array.from(iterable) is not properly supported.
return [...iterable];
}
static get<T>(m: List<T>, k: number): T { return m[k]; }
static set<T>(m: List<T>, k: number, v: T) { m[k] = v; }
static clone<T>(array: List<T>): T[] { return array.slice(0); }
Expand Down Expand Up @@ -257,7 +262,7 @@ export class ListWrapper {
static splice<T>(l: List<T>, from: number, length: number): List<T> {
return l.splice(from, length);
}
static sort<T>(l: List<T>, compareFn?: (a: T, b: T) => number) {
static sort<T>(l: List<T>, compareFn?: CompareFn<T>) {
if (isPresent(compareFn)) {
l.sort(compareFn);
} else {
Expand Down
2 changes: 2 additions & 0 deletions modules/angular2/src/facade/lang.dart
Original file line number Diff line number Diff line change
Expand Up @@ -281,5 +281,7 @@ class DateWrapper {
}
}

int compare(a, b) => Comparable.compare(a, b);

// needed to match the exports from lang.js
var global = null;
5 changes: 5 additions & 0 deletions modules/angular2/src/facade/lang.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,8 @@ export class DateWrapper {
static now(): Date { return new Date(); }
static toJson(date: Date): string { return date.toJSON(); }
}

export function compare(a, b): int {
if (a === b) return 0;
return a < b ? -1 : 1;
}
Loading
X Tutup