refactor(pipes): removed pipes from properties
BREAKING CHANGE: This PR remove an ability to use pipes in the properties config. Instead, inject the pipe registry.
This commit is contained in:
parent
ad7aca631d
commit
20a8f0dbe5
@ -145,7 +145,7 @@ export class ChangeDetectorJITGenerator {
|
|||||||
_getNonNullPipeNames(): List<string> {
|
_getNonNullPipeNames(): List<string> {
|
||||||
var pipes = [];
|
var pipes = [];
|
||||||
this.records.forEach((r) => {
|
this.records.forEach((r) => {
|
||||||
if (r.mode === RecordType.PIPE || r.mode === RecordType.BINDING_PIPE) {
|
if (r.isPipeRecord()) {
|
||||||
pipes.push(this._pipeNames[r.selfIndex]);
|
pipes.push(this._pipeNames[r.selfIndex]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -245,7 +245,7 @@ export class ChangeDetectorJITGenerator {
|
|||||||
var change = this._changeNames[r.selfIndex];
|
var change = this._changeNames[r.selfIndex];
|
||||||
|
|
||||||
var pipe = this._pipeNames[r.selfIndex];
|
var pipe = this._pipeNames[r.selfIndex];
|
||||||
var cdRef = r.mode === RecordType.BINDING_PIPE ? "this.ref" : "null";
|
var cdRef = "this.ref";
|
||||||
|
|
||||||
var protoIndex = r.selfIndex - 1;
|
var protoIndex = r.selfIndex - 1;
|
||||||
var pipeType = r.name;
|
var pipeType = r.name;
|
||||||
|
@ -269,14 +269,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
|||||||
if (isPresent(storedPipe)) {
|
if (isPresent(storedPipe)) {
|
||||||
storedPipe.onDestroy();
|
storedPipe.onDestroy();
|
||||||
}
|
}
|
||||||
|
var pipe = this.pipeRegistry.get(proto.name, context, this.ref);
|
||||||
// Currently, only pipes that used in bindings in the template get
|
|
||||||
// the changeDetectorRef of the encompassing component.
|
|
||||||
//
|
|
||||||
// In the future, pipes declared in the bind configuration should
|
|
||||||
// be able to access the changeDetectorRef of that component.
|
|
||||||
var cdr = proto.mode === RecordType.BINDING_PIPE ? this.ref : null;
|
|
||||||
var pipe = this.pipeRegistry.get(proto.name, context, cdr);
|
|
||||||
this._writePipe(proto, pipe);
|
this._writePipe(proto, pipe);
|
||||||
return pipe;
|
return pipe;
|
||||||
}
|
}
|
||||||
|
@ -141,11 +141,8 @@ export class KeyedAccess extends AST {
|
|||||||
visit(visitor: AstVisitor) { return visitor.visitKeyedAccess(this); }
|
visit(visitor: AstVisitor) { return visitor.visitKeyedAccess(this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Pipe extends AST {
|
export class BindingPipe extends AST {
|
||||||
constructor(public exp: AST, public name: string, public args: List<any>,
|
constructor(public exp: AST, public name: string, public args: List<any>) { super(); }
|
||||||
public inBinding: boolean) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor: AstVisitor) { return visitor.visitPipe(this); }
|
visit(visitor: AstVisitor) { return visitor.visitPipe(this); }
|
||||||
}
|
}
|
||||||
@ -336,7 +333,7 @@ export interface AstVisitor {
|
|||||||
visitChain(ast: Chain): any;
|
visitChain(ast: Chain): any;
|
||||||
visitConditional(ast: Conditional): any;
|
visitConditional(ast: Conditional): any;
|
||||||
visitIf(ast: If): any;
|
visitIf(ast: If): any;
|
||||||
visitPipe(ast: Pipe): any;
|
visitPipe(ast: BindingPipe): any;
|
||||||
visitFunctionCall(ast: FunctionCall): any;
|
visitFunctionCall(ast: FunctionCall): any;
|
||||||
visitImplicitReceiver(ast: ImplicitReceiver): any;
|
visitImplicitReceiver(ast: ImplicitReceiver): any;
|
||||||
visitInterpolation(ast: Interpolation): any;
|
visitInterpolation(ast: Interpolation): any;
|
||||||
@ -394,8 +391,8 @@ export class AstTransformer implements AstVisitor {
|
|||||||
ast.falseExp.visit(this));
|
ast.falseExp.visit(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitPipe(ast: Pipe) {
|
visitPipe(ast: BindingPipe) {
|
||||||
return new Pipe(ast.exp.visit(this), ast.name, this.visitAll(ast.args), ast.inBinding);
|
return new BindingPipe(ast.exp.visit(this), ast.name, this.visitAll(ast.args));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitKeyedAccess(ast: KeyedAccess) {
|
visitKeyedAccess(ast: KeyedAccess) {
|
||||||
|
@ -34,7 +34,7 @@ import {
|
|||||||
PrefixNot,
|
PrefixNot,
|
||||||
Conditional,
|
Conditional,
|
||||||
If,
|
If,
|
||||||
Pipe,
|
BindingPipe,
|
||||||
Assignment,
|
Assignment,
|
||||||
Chain,
|
Chain,
|
||||||
KeyedAccess,
|
KeyedAccess,
|
||||||
@ -73,15 +73,6 @@ export class Parser {
|
|||||||
return new ASTWithSource(ast, input, location);
|
return new ASTWithSource(ast, input, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
addPipes(bindingAst: ASTWithSource, pipes: List<string>): ASTWithSource {
|
|
||||||
if (ListWrapper.isEmpty(pipes)) return bindingAst;
|
|
||||||
|
|
||||||
var res: AST = ListWrapper.reduce(
|
|
||||||
pipes, (result, currentPipeName) => new Pipe(result, currentPipeName, [], false),
|
|
||||||
bindingAst.ast);
|
|
||||||
return new ASTWithSource(res, bindingAst.source, bindingAst.location);
|
|
||||||
}
|
|
||||||
|
|
||||||
parseTemplateBindings(input: string, location: any): List<TemplateBinding> {
|
parseTemplateBindings(input: string, location: any): List<TemplateBinding> {
|
||||||
var tokens = this._lexer.tokenize(input);
|
var tokens = this._lexer.tokenize(input);
|
||||||
return new _ParseAST(input, location, tokens, this._reflector, false).parseTemplateBindings();
|
return new _ParseAST(input, location, tokens, this._reflector, false).parseTemplateBindings();
|
||||||
@ -224,7 +215,7 @@ class _ParseAST {
|
|||||||
while (this.optionalCharacter($COLON)) {
|
while (this.optionalCharacter($COLON)) {
|
||||||
args.push(this.parsePipe());
|
args.push(this.parsePipe());
|
||||||
}
|
}
|
||||||
result = new Pipe(result, name, args, true);
|
result = new BindingPipe(result, name, args);
|
||||||
} while (this.optionalOperator("|"));
|
} while (this.optionalOperator("|"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,12 +15,10 @@ import {
|
|||||||
isArray
|
isArray
|
||||||
} from 'angular2/src/facade/lang';
|
} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
import {WrappedValue, Pipe, PipeFactory} from './pipe';
|
import {WrappedValue, Pipe, BasePipe, PipeFactory} from './pipe';
|
||||||
|
|
||||||
@CONST()
|
@CONST()
|
||||||
export class IterableChangesFactory extends PipeFactory {
|
export class IterableChangesFactory implements PipeFactory {
|
||||||
constructor() { super(); }
|
|
||||||
|
|
||||||
supports(obj): boolean { return IterableChanges.supportsObj(obj); }
|
supports(obj): boolean { return IterableChanges.supportsObj(obj); }
|
||||||
|
|
||||||
create(cdRef): Pipe { return new IterableChanges(); }
|
create(cdRef): Pipe { return new IterableChanges(); }
|
||||||
@ -29,7 +27,7 @@ export class IterableChangesFactory extends PipeFactory {
|
|||||||
/**
|
/**
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
export class IterableChanges extends Pipe {
|
export class IterableChanges extends BasePipe {
|
||||||
private _collection = null;
|
private _collection = null;
|
||||||
private _length: int = null;
|
private _length: int = null;
|
||||||
// Keeps track of the used records at any point in time (during & across `_check()` calls)
|
// Keeps track of the used records at any point in time (during & across `_check()` calls)
|
||||||
@ -95,7 +93,7 @@ export class IterableChanges extends Pipe {
|
|||||||
if (this.check(collection)) {
|
if (this.check(collection)) {
|
||||||
return WrappedValue.wrap(this);
|
return WrappedValue.wrap(this);
|
||||||
} else {
|
} else {
|
||||||
return this;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {isBlank, isPresent, Json} from 'angular2/src/facade/lang';
|
import {isBlank, isPresent, Json} from 'angular2/src/facade/lang';
|
||||||
import {Pipe, PipeFactory} from './pipe';
|
import {Pipe, BasePipe, PipeFactory} from './pipe';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements json transforms to any object.
|
* Implements json transforms to any object.
|
||||||
@ -26,9 +26,7 @@ import {Pipe, PipeFactory} from './pipe';
|
|||||||
*
|
*
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
export class JsonPipe extends Pipe {
|
export class JsonPipe extends BasePipe {
|
||||||
supports(obj): boolean { return true; }
|
|
||||||
|
|
||||||
transform(value): string { return Json.stringify(value); }
|
transform(value): string { return Json.stringify(value); }
|
||||||
|
|
||||||
create(cdRef): Pipe { return this }
|
create(cdRef): Pipe { return this }
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {stringify, looseIdentical, isJsObject, CONST} from 'angular2/src/facade/lang';
|
import {stringify, looseIdentical, isJsObject, CONST} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
import {WrappedValue, Pipe, PipeFactory} from './pipe';
|
import {WrappedValue, BasePipe, Pipe, PipeFactory} from './pipe';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
@CONST()
|
@CONST()
|
||||||
export class KeyValueChangesFactory extends PipeFactory {
|
export class KeyValueChangesFactory implements PipeFactory {
|
||||||
constructor() { super(); }
|
|
||||||
|
|
||||||
supports(obj): boolean { return KeyValueChanges.supportsObj(obj); }
|
supports(obj): boolean { return KeyValueChanges.supportsObj(obj); }
|
||||||
|
|
||||||
create(cdRef): Pipe { return new KeyValueChanges(); }
|
create(cdRef): Pipe { return new KeyValueChanges(); }
|
||||||
@ -18,7 +16,7 @@ export class KeyValueChangesFactory extends PipeFactory {
|
|||||||
/**
|
/**
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
export class KeyValueChanges extends Pipe {
|
export class KeyValueChanges extends BasePipe {
|
||||||
private _records: Map<any, any> = new Map();
|
private _records: Map<any, any> = new Map();
|
||||||
private _mapHead: KVChangeRecord = null;
|
private _mapHead: KVChangeRecord = null;
|
||||||
private _previousMapHead: KVChangeRecord = null;
|
private _previousMapHead: KVChangeRecord = null;
|
||||||
@ -37,7 +35,7 @@ export class KeyValueChanges extends Pipe {
|
|||||||
if (this.check(map)) {
|
if (this.check(map)) {
|
||||||
return WrappedValue.wrap(this);
|
return WrappedValue.wrap(this);
|
||||||
} else {
|
} else {
|
||||||
return this;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import {Pipe} from './pipe';
|
|||||||
*
|
*
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
export class LowerCasePipe extends Pipe {
|
export class LowerCasePipe implements Pipe {
|
||||||
_latestValue: string = null;
|
_latestValue: string = null;
|
||||||
|
|
||||||
supports(str): boolean { return isString(str); }
|
supports(str): boolean { return isString(str); }
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import {isBlank, CONST} from 'angular2/src/facade/lang';
|
import {isBlank, CONST} from 'angular2/src/facade/lang';
|
||||||
import {Pipe, WrappedValue, PipeFactory} from './pipe';
|
import {Pipe, BasePipe, WrappedValue, PipeFactory} from './pipe';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
@CONST()
|
@CONST()
|
||||||
export class NullPipeFactory extends PipeFactory {
|
export class NullPipeFactory implements PipeFactory {
|
||||||
constructor() { super(); }
|
|
||||||
|
|
||||||
supports(obj): boolean { return NullPipe.supportsObj(obj); }
|
supports(obj): boolean { return NullPipe.supportsObj(obj); }
|
||||||
|
|
||||||
create(cdRef): Pipe { return new NullPipe(); }
|
create(cdRef): Pipe { return new NullPipe(); }
|
||||||
@ -16,7 +14,7 @@ export class NullPipeFactory extends PipeFactory {
|
|||||||
/**
|
/**
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
export class NullPipe extends Pipe {
|
export class NullPipe extends BasePipe {
|
||||||
called: boolean = false;
|
called: boolean = false;
|
||||||
|
|
||||||
static supportsObj(obj): boolean { return isBlank(obj); }
|
static supportsObj(obj): boolean { return isBlank(obj); }
|
||||||
|
@ -29,14 +29,14 @@ import {ChangeDetectorRef} from '../change_detector_ref';
|
|||||||
*
|
*
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
export class ObservablePipe extends Pipe {
|
export class ObservablePipe implements Pipe {
|
||||||
_latestValue: Object = null;
|
_latestValue: Object = null;
|
||||||
_latestReturnedValue: Object = null;
|
_latestReturnedValue: Object = null;
|
||||||
|
|
||||||
_subscription: Object = null;
|
_subscription: Object = null;
|
||||||
_observable: Observable = null;
|
_observable: Observable = null;
|
||||||
|
|
||||||
constructor(public _ref: ChangeDetectorRef) { super(); }
|
constructor(public _ref: ChangeDetectorRef) {}
|
||||||
|
|
||||||
supports(obs): boolean { return ObservableWrapper.isObservable(obs); }
|
supports(obs): boolean { return ObservableWrapper.isObservable(obs); }
|
||||||
|
|
||||||
@ -91,9 +91,7 @@ export class ObservablePipe extends Pipe {
|
|||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
@CONST()
|
@CONST()
|
||||||
export class ObservablePipeFactory extends PipeFactory {
|
export class ObservablePipeFactory implements PipeFactory {
|
||||||
constructor() { super(); }
|
|
||||||
|
|
||||||
supports(obs): boolean { return ObservableWrapper.isObservable(obs); }
|
supports(obs): boolean { return ObservableWrapper.isObservable(obs); }
|
||||||
|
|
||||||
create(cdRef): Pipe { return new ObservablePipe(cdRef); }
|
create(cdRef): Pipe { return new ObservablePipe(cdRef); }
|
||||||
|
@ -36,11 +36,13 @@ var _wrappedIndex = 0;
|
|||||||
* #Example
|
* #Example
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* class DoublePipe extends Pipe {
|
* class DoublePipe implements Pipe {
|
||||||
* supports(obj) {
|
* supports(obj) {
|
||||||
* return true;
|
* return true;
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
|
* onDestroy() {}
|
||||||
|
*
|
||||||
* transform(value) {
|
* transform(value) {
|
||||||
* return `${value}${value}`;
|
* return `${value}${value}`;
|
||||||
* }
|
* }
|
||||||
@ -49,24 +51,34 @@ var _wrappedIndex = 0;
|
|||||||
*
|
*
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
export class Pipe {
|
export interface Pipe {
|
||||||
supports(obj): boolean { return false; }
|
supports(obj): boolean;
|
||||||
onDestroy() {}
|
onDestroy(): void;
|
||||||
transform(value: any): any { return null; }
|
transform(value: any): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: vsavkin: make it an interface
|
/**
|
||||||
@CONST()
|
* Provides default implementation of supports and onDestroy.
|
||||||
export class PipeFactory {
|
*
|
||||||
supports(obs): boolean {
|
* #Example
|
||||||
_abstract();
|
*
|
||||||
return false;
|
* ```
|
||||||
}
|
* class DoublePipe extends BasePipe {*
|
||||||
|
* transform(value) {
|
||||||
|
* return `${value}${value}`;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export class BasePipe implements Pipe {
|
||||||
|
supports(obj): boolean { return true; }
|
||||||
|
onDestroy(): void {}
|
||||||
|
transform(value: any): any { return _abstract(); }
|
||||||
|
}
|
||||||
|
|
||||||
create(cdRef): Pipe {
|
export interface PipeFactory {
|
||||||
_abstract();
|
supports(obs): boolean;
|
||||||
return null;
|
create(cdRef): Pipe;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _abstract() {
|
function _abstract() {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang';
|
import {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang';
|
||||||
import {Pipe} from './pipe';
|
import {Pipe, PipeFactory} from './pipe';
|
||||||
import {Injectable} from 'angular2/src/di/decorators';
|
import {Injectable} from 'angular2/src/di/decorators';
|
||||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||||
|
|
||||||
@ -8,18 +8,31 @@ import {ChangeDetectorRef} from '../change_detector_ref';
|
|||||||
export class PipeRegistry {
|
export class PipeRegistry {
|
||||||
constructor(public config) {}
|
constructor(public config) {}
|
||||||
|
|
||||||
get(type: string, obj, cdRef: ChangeDetectorRef): Pipe {
|
get(type: string, obj, cdRef?: ChangeDetectorRef, existingPipe?: Pipe): Pipe {
|
||||||
var listOfConfigs = this.config[type];
|
if (isPresent(existingPipe) && existingPipe.supports(obj)) return existingPipe;
|
||||||
if (isBlank(listOfConfigs)) {
|
|
||||||
|
if (isPresent(existingPipe)) existingPipe.onDestroy();
|
||||||
|
|
||||||
|
var factories = this._getListOfFactories(type, obj);
|
||||||
|
var factory = this._getMatchingFactory(factories, type, obj);
|
||||||
|
|
||||||
|
return factory.create(cdRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getListOfFactories(type: string, obj: any): PipeFactory[] {
|
||||||
|
var listOfFactories = this.config[type];
|
||||||
|
if (isBlank(listOfFactories)) {
|
||||||
throw new BaseException(`Cannot find '${type}' pipe supporting object '${obj}'`);
|
throw new BaseException(`Cannot find '${type}' pipe supporting object '${obj}'`);
|
||||||
}
|
}
|
||||||
|
return listOfFactories;
|
||||||
|
}
|
||||||
|
|
||||||
var matchingConfig = ListWrapper.find(listOfConfigs, (pipeConfig) => pipeConfig.supports(obj));
|
private _getMatchingFactory(listOfFactories: PipeFactory[], type: string, obj: any): PipeFactory {
|
||||||
|
var matchingFactory =
|
||||||
if (isBlank(matchingConfig)) {
|
ListWrapper.find(listOfFactories, pipeFactory => pipeFactory.supports(obj));
|
||||||
|
if (isBlank(matchingFactory)) {
|
||||||
throw new BaseException(`Cannot find '${type}' pipe supporting object '${obj}'`);
|
throw new BaseException(`Cannot find '${type}' pipe supporting object '${obj}'`);
|
||||||
}
|
}
|
||||||
|
return matchingFactory;
|
||||||
return matchingConfig.create(cdRef);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,12 +28,12 @@ import {ChangeDetectorRef} from '../change_detector_ref';
|
|||||||
*
|
*
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
export class PromisePipe extends Pipe {
|
export class PromisePipe implements Pipe {
|
||||||
_latestValue: Object = null;
|
_latestValue: Object = null;
|
||||||
_latestReturnedValue: Object = null;
|
_latestReturnedValue: Object = null;
|
||||||
_sourcePromise: Promise<any>;
|
_sourcePromise: Promise<any>;
|
||||||
|
|
||||||
constructor(public _ref: ChangeDetectorRef) { super(); }
|
constructor(public _ref: ChangeDetectorRef) {}
|
||||||
|
|
||||||
supports(promise): boolean { return isPromise(promise); }
|
supports(promise): boolean { return isPromise(promise); }
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import {Pipe} from './pipe';
|
|||||||
*
|
*
|
||||||
* @exportedAs angular2/pipes
|
* @exportedAs angular2/pipes
|
||||||
*/
|
*/
|
||||||
export class UpperCasePipe extends Pipe {
|
export class UpperCasePipe implements Pipe {
|
||||||
_latestValue: string = null;
|
_latestValue: string = null;
|
||||||
|
|
||||||
supports(str): boolean { return isString(str); }
|
supports(str): boolean { return isString(str); }
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
Chain,
|
Chain,
|
||||||
Conditional,
|
Conditional,
|
||||||
If,
|
If,
|
||||||
Pipe,
|
BindingPipe,
|
||||||
FunctionCall,
|
FunctionCall,
|
||||||
ImplicitReceiver,
|
ImplicitReceiver,
|
||||||
Interpolation,
|
Interpolation,
|
||||||
@ -179,10 +179,9 @@ class _ConvertAstIntoProtoRecords implements AstVisitor {
|
|||||||
null, 0);
|
null, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitPipe(ast: Pipe) {
|
visitPipe(ast: BindingPipe) {
|
||||||
var value = ast.exp.visit(this);
|
var value = ast.exp.visit(this);
|
||||||
var type = ast.inBinding ? RecordType.BINDING_PIPE : RecordType.PIPE;
|
return this._addRecord(RecordType.PIPE, ast.name, ast.name, [], null, value);
|
||||||
return this._addRecord(type, ast.name, ast.name, [], null, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visitKeyedAccess(ast: KeyedAccess) {
|
visitKeyedAccess(ast: KeyedAccess) {
|
||||||
|
@ -12,7 +12,6 @@ export enum RecordType {
|
|||||||
INVOKE_CLOSURE,
|
INVOKE_CLOSURE,
|
||||||
KEYED_ACCESS,
|
KEYED_ACCESS,
|
||||||
PIPE,
|
PIPE,
|
||||||
BINDING_PIPE,
|
|
||||||
INTERPOLATE,
|
INTERPOLATE,
|
||||||
SAFE_PROPERTY,
|
SAFE_PROPERTY,
|
||||||
SAFE_INVOKE_METHOD,
|
SAFE_INVOKE_METHOD,
|
||||||
@ -30,9 +29,7 @@ export class ProtoRecord {
|
|||||||
return this.mode === RecordType.INTERPOLATE || this.mode === RecordType.PRIMITIVE_OP;
|
return this.mode === RecordType.INTERPOLATE || this.mode === RecordType.PRIMITIVE_OP;
|
||||||
}
|
}
|
||||||
|
|
||||||
isPipeRecord(): boolean {
|
isPipeRecord(): boolean { return this.mode === RecordType.PIPE; }
|
||||||
return this.mode === RecordType.PIPE || this.mode === RecordType.BINDING_PIPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
isLifeCycleRecord(): boolean { return this.mode === RecordType.DIRECTIVE_LIFECYCLE; }
|
isLifeCycleRecord(): boolean { return this.mode === RecordType.DIRECTIVE_LIFECYCLE; }
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,17 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
|||||||
export class CSSClass {
|
export class CSSClass {
|
||||||
_domEl;
|
_domEl;
|
||||||
_pipe;
|
_pipe;
|
||||||
_prevRawClass;
|
_rawClass;
|
||||||
rawClass;
|
|
||||||
constructor(private _pipeRegistry: PipeRegistry, ngEl: ElementRef) {
|
constructor(private _pipeRegistry: PipeRegistry, ngEl: ElementRef) {
|
||||||
this._domEl = ngEl.domElement;
|
this._domEl = ngEl.domElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set rawClass(v) {
|
||||||
|
this._rawClass = v;
|
||||||
|
this._pipe = this._pipeRegistry.get('keyValDiff', this._rawClass);
|
||||||
|
}
|
||||||
|
|
||||||
_toggleClass(className, enabled): void {
|
_toggleClass(className, enabled): void {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
DOM.addClass(this._domEl, className);
|
DOM.addClass(this._domEl, className);
|
||||||
@ -23,19 +28,15 @@ export class CSSClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onCheck() {
|
onCheck() {
|
||||||
if (this.rawClass != this._prevRawClass) {
|
var diff = this._pipe.transform(this._rawClass);
|
||||||
this._prevRawClass = this.rawClass;
|
if (isPresent(diff)) this._applyChanges(diff.wrapped);
|
||||||
this._pipe = isPresent(this.rawClass) ?
|
}
|
||||||
this._pipeRegistry.get('keyValDiff', this.rawClass, null) :
|
|
||||||
null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPresent(this._pipe) && this._pipe.check(this.rawClass)) {
|
private _applyChanges(diff) {
|
||||||
this._pipe.forEachAddedItem(
|
if (isPresent(diff)) {
|
||||||
(record) => { this._toggleClass(record.key, record.currentValue); });
|
diff.forEachAddedItem((record) => { this._toggleClass(record.key, record.currentValue); });
|
||||||
this._pipe.forEachChangedItem(
|
diff.forEachChangedItem((record) => { this._toggleClass(record.key, record.currentValue); });
|
||||||
(record) => { this._toggleClass(record.key, record.currentValue); });
|
diff.forEachRemovedItem((record) => {
|
||||||
this._pipe.forEachRemovedItem((record) => {
|
|
||||||
if (record.previousValue) {
|
if (record.previousValue) {
|
||||||
DOM.removeClass(this._domEl, record.key);
|
DOM.removeClass(this._domEl, record.key);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
import {Directive} from 'angular2/annotations';
|
import {Directive} from 'angular2/annotations';
|
||||||
import {ViewContainerRef, ViewRef, ProtoViewRef} from 'angular2/core';
|
import {
|
||||||
|
ViewContainerRef,
|
||||||
|
ViewRef,
|
||||||
|
ProtoViewRef,
|
||||||
|
PipeRegistry,
|
||||||
|
onCheck,
|
||||||
|
Pipe
|
||||||
|
} from 'angular2/angular2';
|
||||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,17 +41,25 @@ import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
|||||||
*
|
*
|
||||||
* @exportedAs angular2/directives
|
* @exportedAs angular2/directives
|
||||||
*/
|
*/
|
||||||
@Directive(
|
@Directive({selector: '[ng-for][ng-for-of]', properties: ['ngForOf'], lifecycle: [onCheck]})
|
||||||
{selector: '[ng-for][ng-for-of]', properties: ['iterableChanges: ngForOf | iterableDiff']})
|
|
||||||
export class NgFor {
|
export class NgFor {
|
||||||
viewContainer: ViewContainerRef;
|
_ngForOf: any;
|
||||||
protoViewRef: ProtoViewRef;
|
_pipe: Pipe;
|
||||||
constructor(viewContainer: ViewContainerRef, protoViewRef: ProtoViewRef) {
|
|
||||||
this.viewContainer = viewContainer;
|
constructor(private viewContainer: ViewContainerRef, private protoViewRef: ProtoViewRef,
|
||||||
this.protoViewRef = protoViewRef;
|
private pipes: PipeRegistry) {}
|
||||||
|
|
||||||
|
set ngForOf(value: any) {
|
||||||
|
this._ngForOf = value;
|
||||||
|
this._pipe = this.pipes.get("iterableDiff", value, null, this._pipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
set iterableChanges(changes) {
|
onCheck() {
|
||||||
|
var diff = this._pipe.transform(this._ngForOf);
|
||||||
|
if (isPresent(diff)) this._applyChanges(diff.wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _applyChanges(changes) {
|
||||||
if (isBlank(changes)) {
|
if (isBlank(changes)) {
|
||||||
this.viewContainer.clear();
|
this.viewContainer.clear();
|
||||||
return;
|
return;
|
||||||
@ -67,11 +82,11 @@ export class NgFor {
|
|||||||
NgFor.bulkInsert(insertTuples, this.viewContainer, this.protoViewRef);
|
NgFor.bulkInsert(insertTuples, this.viewContainer, this.protoViewRef);
|
||||||
|
|
||||||
for (var i = 0; i < insertTuples.length; i++) {
|
for (var i = 0; i < insertTuples.length; i++) {
|
||||||
this.perViewChange(insertTuples[i].view, insertTuples[i].record);
|
this._perViewChange(insertTuples[i].view, insertTuples[i].record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
perViewChange(view, record) {
|
private _perViewChange(view, record) {
|
||||||
view.setLocal('\$implicit', record.item);
|
view.setLocal('\$implicit', record.item);
|
||||||
view.setLocal('index', record.currentIndex);
|
view.setLocal('index', record.currentIndex);
|
||||||
}
|
}
|
||||||
|
@ -147,8 +147,7 @@ export class DirectiveParser implements CompileStep {
|
|||||||
|
|
||||||
// Bindings are optional, so this binding only needs to be set up if an expression is given.
|
// Bindings are optional, so this binding only needs to be set up if an expression is given.
|
||||||
if (isPresent(bindingAst)) {
|
if (isPresent(bindingAst)) {
|
||||||
var fullExpAstWithBindPipes = this._parser.addPipes(bindingAst, pipes);
|
directiveBinderBuilder.bindProperty(dirProperty, bindingAst);
|
||||||
directiveBinderBuilder.bindProperty(dirProperty, fullExpAstWithBindPipes);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
24
modules/angular2/src/test_lib/spies.dart
Normal file
24
modules/angular2/src/test_lib/spies.dart
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
library test_lib.spies;
|
||||||
|
|
||||||
|
import 'package:angular2/change_detection.dart';
|
||||||
|
import './test_lib.dart';
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
class SpyChangeDetector extends SpyObject implements ChangeDetector {
|
||||||
|
noSuchMethod(m) => super.noSuchMethod(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
class SpyProtoChangeDetector extends SpyObject implements ProtoChangeDetector {
|
||||||
|
noSuchMethod(m) => super.noSuchMethod(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
class SpyPipe extends SpyObject implements Pipe {
|
||||||
|
noSuchMethod(m) => super.noSuchMethod(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
class SpyPipeFactory extends SpyObject implements PipeFactory {
|
||||||
|
noSuchMethod(m) => super.noSuchMethod(m);
|
||||||
|
}
|
@ -3,45 +3,19 @@ import {
|
|||||||
ProtoChangeDetector,
|
ProtoChangeDetector,
|
||||||
DynamicChangeDetector
|
DynamicChangeDetector
|
||||||
} from 'angular2/change_detection';
|
} from 'angular2/change_detection';
|
||||||
|
import {BasePipe} from 'angular2/src/change_detection/pipes/pipe';
|
||||||
import {SpyObject, proxy} from './test_lib';
|
import {SpyObject, proxy} from './test_lib';
|
||||||
|
|
||||||
// Remove dummy methods after https://github.com/angular/ts2dart/issues/209 is fixed.
|
export class SpyChangeDetector extends SpyObject {
|
||||||
@proxy
|
constructor() { super(DynamicChangeDetector); }
|
||||||
export class SpyChangeDetector extends SpyObject implements ChangeDetector {
|
|
||||||
parent: ChangeDetector;
|
|
||||||
mode: string;
|
|
||||||
|
|
||||||
constructor() { super(DynamicChangeDetector, true); }
|
|
||||||
|
|
||||||
addChild(cd: ChangeDetector): void { return this.spy("addChild")(cd); }
|
|
||||||
|
|
||||||
addShadowDomChild(cd: ChangeDetector): void { return this.spy("addShadowDomChild")(cd); }
|
|
||||||
|
|
||||||
removeChild(cd: ChangeDetector): void { return this.spy("removeChild")(cd); }
|
|
||||||
|
|
||||||
removeShadowDomChild(cd: ChangeDetector): void { return this.spy("removeShadowDomChild")(cd); }
|
|
||||||
|
|
||||||
remove(): void { return this.spy("remove")(); }
|
|
||||||
|
|
||||||
hydrate(context: any, locals: any, directives: any): void {
|
|
||||||
return this.spy("hydrate")(context, locals, directives);
|
|
||||||
}
|
|
||||||
|
|
||||||
dehydrate(): void { return this.spy("dehydrate")(); }
|
|
||||||
|
|
||||||
markPathToRootAsCheckOnce(): void { return this.spy("markPathToRootAsCheckOnce")(); }
|
|
||||||
|
|
||||||
detectChanges(): void { return this.spy("detectChanges")(); }
|
|
||||||
|
|
||||||
checkNoChanges(): void { return this.spy("checkNoChanges")(); }
|
|
||||||
|
|
||||||
noSuchMethod(m) { return super.noSuchMethod(m) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove dummy methods after https://github.com/angular/ts2dart/issues/209 is fixed.
|
export class SpyProtoChangeDetector extends SpyObject {
|
||||||
@proxy
|
constructor() { super(DynamicChangeDetector); }
|
||||||
export class SpyProtoChangeDetector extends SpyObject implements ProtoChangeDetector {
|
}
|
||||||
constructor() { super(DynamicChangeDetector, true); }
|
|
||||||
|
|
||||||
instantiate(v: any): any { return this.spy("instantiate")(v); }
|
export class SpyPipe extends SpyObject {
|
||||||
}
|
constructor() { super(BasePipe); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpyPipeFactory extends SpyObject {}
|
@ -187,7 +187,7 @@ class SpyFunction extends gns.SpyFunction {
|
|||||||
class SpyObject extends gns.SpyObject {
|
class SpyObject extends gns.SpyObject {
|
||||||
final Map<String, SpyFunction> _spyFuncs = {};
|
final Map<String, SpyFunction> _spyFuncs = {};
|
||||||
|
|
||||||
SpyObject([arg, arg2]) {}
|
SpyObject([arg]) {}
|
||||||
|
|
||||||
SpyFunction spy(String funcName) =>
|
SpyFunction spy(String funcName) =>
|
||||||
_spyFuncs.putIfAbsent(funcName, () => new SpyFunction(funcName));
|
_spyFuncs.putIfAbsent(funcName, () => new SpyFunction(funcName));
|
||||||
|
@ -274,7 +274,7 @@ export interface GuinessCompatibleSpy extends jasmine.Spy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class SpyObject {
|
export class SpyObject {
|
||||||
constructor(type = null, forceSpyCreation: boolean = false) {
|
constructor(type = null) {
|
||||||
if (type) {
|
if (type) {
|
||||||
for (var prop in type.prototype) {
|
for (var prop in type.prototype) {
|
||||||
var m = null;
|
var m = null;
|
||||||
@ -287,11 +287,7 @@ export class SpyObject {
|
|||||||
// should not matter.
|
// should not matter.
|
||||||
}
|
}
|
||||||
if (typeof m === 'function') {
|
if (typeof m === 'function') {
|
||||||
if (forceSpyCreation) {
|
this.spy(prop);
|
||||||
this.createSpy(prop);
|
|
||||||
} else {
|
|
||||||
this.spy(prop);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -301,14 +297,8 @@ export class SpyObject {
|
|||||||
|
|
||||||
spy(name) {
|
spy(name) {
|
||||||
if (!this[name]) {
|
if (!this[name]) {
|
||||||
return this.createSpy(name);
|
this[name] = this._createGuinnessCompatibleSpy(name);
|
||||||
} else {
|
|
||||||
return this[name];
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
createSpy(name) {
|
|
||||||
this[name] = this._createGuinnessCompatibleSpy(name);
|
|
||||||
return this[name];
|
return this[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +223,7 @@ class _CodegenState {
|
|||||||
List<String> _getNonNullPipeNames() {
|
List<String> _getNonNullPipeNames() {
|
||||||
return _records
|
return _records
|
||||||
.where((r) =>
|
.where((r) =>
|
||||||
r.mode == RecordType.PIPE || r.mode == RecordType.BINDING_PIPE)
|
r.mode == RecordType.PIPE)
|
||||||
.map((r) => _pipeNames[r.selfIndex])
|
.map((r) => _pipeNames[r.selfIndex])
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
@ -310,7 +310,7 @@ class _CodegenState {
|
|||||||
var change = _changeNames[r.selfIndex];
|
var change = _changeNames[r.selfIndex];
|
||||||
|
|
||||||
var pipe = _pipeNames[r.selfIndex];
|
var pipe = _pipeNames[r.selfIndex];
|
||||||
var cdRef = r.mode == RecordType.BINDING_PIPE ? 'this.ref' : 'null';
|
var cdRef = 'this.ref';
|
||||||
|
|
||||||
var protoIndex = r.selfIndex - 1;
|
var protoIndex = r.selfIndex - 1;
|
||||||
var pipeType = r.name;
|
var pipeType = r.name;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
///<reference path="../../src/change_detection/pipes/pipe.ts"/>
|
||||||
import {
|
import {
|
||||||
ddescribe,
|
ddescribe,
|
||||||
describe,
|
describe,
|
||||||
@ -853,28 +854,19 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class CountingPipe extends Pipe {
|
class CountingPipe implements Pipe {
|
||||||
state: number;
|
state: number = 0;
|
||||||
|
|
||||||
constructor() {
|
onDestroy() {}
|
||||||
super();
|
|
||||||
this.state = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
supports(newValue) { return true; }
|
supports(newValue) { return true; }
|
||||||
|
|
||||||
transform(value) { return `${value} state:${this.state ++}`; }
|
transform(value) { return `${value} state:${this.state ++}`; }
|
||||||
}
|
}
|
||||||
|
|
||||||
class OncePipe extends Pipe {
|
class OncePipe implements Pipe {
|
||||||
called: boolean;
|
called: boolean = false;
|
||||||
destroyCalled: boolean;
|
destroyCalled: boolean = false;
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.called = false;
|
|
||||||
this.destroyCalled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
supports(newValue) { return !this.called; }
|
supports(newValue) { return !this.called; }
|
||||||
|
|
||||||
@ -886,11 +878,19 @@ class OncePipe extends Pipe {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class IdentityPipe extends Pipe {
|
class IdentityPipe implements Pipe {
|
||||||
|
supports(obj): boolean { return true; }
|
||||||
|
|
||||||
|
onDestroy() {}
|
||||||
|
|
||||||
transform(value) { return value; }
|
transform(value) { return value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
class WrappedPipe extends Pipe {
|
class WrappedPipe implements Pipe {
|
||||||
|
supports(obj): boolean { return true; }
|
||||||
|
|
||||||
|
onDestroy() {}
|
||||||
|
|
||||||
transform(value) { return WrappedValue.wrap(value); }
|
transform(value) { return WrappedValue.wrap(value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -907,7 +907,7 @@ class FakePipeRegistry extends PipeRegistry {
|
|||||||
this.numberOfLookups = 0;
|
this.numberOfLookups = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
get(type: string, obj, cdRef) {
|
get(type: string, obj, cdRef?, existingPipe?) {
|
||||||
if (type != this.pipeType) return null;
|
if (type != this.pipeType) return null;
|
||||||
this.numberOfLookups++;
|
this.numberOfLookups++;
|
||||||
this.cdRef = cdRef;
|
this.cdRef = cdRef;
|
||||||
|
@ -6,7 +6,7 @@ import {Parser} from 'angular2/src/change_detection/parser/parser';
|
|||||||
import {Unparser} from './unparser';
|
import {Unparser} from './unparser';
|
||||||
import {Lexer} from 'angular2/src/change_detection/parser/lexer';
|
import {Lexer} from 'angular2/src/change_detection/parser/lexer';
|
||||||
import {Locals} from 'angular2/src/change_detection/parser/locals';
|
import {Locals} from 'angular2/src/change_detection/parser/locals';
|
||||||
import {Pipe, LiteralPrimitive} from 'angular2/src/change_detection/parser/ast';
|
import {BindingPipe, LiteralPrimitive} from 'angular2/src/change_detection/parser/ast';
|
||||||
|
|
||||||
class TestData {
|
class TestData {
|
||||||
constructor(public a?: any, public b?: any, public fnReturnValue?: any) {}
|
constructor(public a?: any, public b?: any, public fnReturnValue?: any) {}
|
||||||
@ -39,8 +39,6 @@ export function main() {
|
|||||||
return createParser().parseInterpolation(text, location);
|
return createParser().parseInterpolation(text, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addPipes(ast, pipes): any { return createParser().addPipes(ast, pipes); }
|
|
||||||
|
|
||||||
function emptyLocals() { return new Locals(null, new Map()); }
|
function emptyLocals() { return new Locals(null, new Map()); }
|
||||||
|
|
||||||
function evalAction(text, passedInContext = null, passedInLocals = null) {
|
function evalAction(text, passedInContext = null, passedInLocals = null) {
|
||||||
@ -412,7 +410,7 @@ export function main() {
|
|||||||
it("should parse pipes", () => {
|
it("should parse pipes", () => {
|
||||||
var originalExp = '"Foo" | uppercase';
|
var originalExp = '"Foo" | uppercase';
|
||||||
var ast = parseBinding(originalExp).ast;
|
var ast = parseBinding(originalExp).ast;
|
||||||
expect(ast).toBeAnInstanceOf(Pipe);
|
expect(ast).toBeAnInstanceOf(BindingPipe);
|
||||||
expect(new Unparser().unparse(ast)).toEqual(`(${originalExp})`);
|
expect(new Unparser().unparse(ast)).toEqual(`(${originalExp})`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -600,7 +598,7 @@ export function main() {
|
|||||||
it('should parse pipes', () => {
|
it('should parse pipes', () => {
|
||||||
var bindings = parseTemplateBindings('key value|pipe');
|
var bindings = parseTemplateBindings('key value|pipe');
|
||||||
var ast = bindings[0].expression.ast;
|
var ast = bindings[0].expression.ast;
|
||||||
expect(ast).toBeAnInstanceOf(Pipe);
|
expect(ast).toBeAnInstanceOf(BindingPipe);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -622,29 +620,6 @@ export function main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('addPipes', () => {
|
|
||||||
it('should return the given ast whe the list of pipes is empty', () => {
|
|
||||||
var ast = parseBinding("1 + 1", "Location");
|
|
||||||
var transformedAst = addPipes(ast, []);
|
|
||||||
expect(transformedAst).toBe(ast);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should append pipe ast nodes', () => {
|
|
||||||
var ast = parseBinding("1 + 1", "Location");
|
|
||||||
var transformedAst = addPipes(ast, ['one', 'two']);
|
|
||||||
expect(transformedAst.ast.name).toEqual("two");
|
|
||||||
expect(transformedAst.ast.exp.name).toEqual("one");
|
|
||||||
expect(transformedAst.ast.exp.exp.operation).toEqual("+");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should preserve location and source', () => {
|
|
||||||
var ast = parseBinding("1 + 1", "Location");
|
|
||||||
var transformedAst = addPipes(ast, ['one', 'two']);
|
|
||||||
expect(transformedAst.source).toEqual("1 + 1");
|
|
||||||
expect(transformedAst.location).toEqual("Location");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('wrapLiteralPrimitive', () => {
|
describe('wrapLiteralPrimitive', () => {
|
||||||
it('should wrap a literal primitive', () => {
|
it('should wrap a literal primitive', () => {
|
||||||
expect(createParser().wrapLiteralPrimitive("foo", null).eval(null, emptyLocals()))
|
expect(createParser().wrapLiteralPrimitive("foo", null).eval(null, emptyLocals()))
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
Conditional,
|
Conditional,
|
||||||
EmptyExpr,
|
EmptyExpr,
|
||||||
If,
|
If,
|
||||||
Pipe,
|
BindingPipe,
|
||||||
FunctionCall,
|
FunctionCall,
|
||||||
ImplicitReceiver,
|
ImplicitReceiver,
|
||||||
Interpolation,
|
Interpolation,
|
||||||
@ -81,7 +81,7 @@ export class Unparser implements AstVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitPipe(ast: Pipe) {
|
visitPipe(ast: BindingPipe) {
|
||||||
this._expression += '(';
|
this._expression += '(';
|
||||||
this._visit(ast.exp);
|
this._visit(ast.exp);
|
||||||
this._expression += ` | ${ast.name}`;
|
this._expression += ` | ${ast.name}`;
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
Conditional,
|
Conditional,
|
||||||
EmptyExpr,
|
EmptyExpr,
|
||||||
If,
|
If,
|
||||||
Pipe,
|
BindingPipe,
|
||||||
ImplicitReceiver,
|
ImplicitReceiver,
|
||||||
Interpolation,
|
Interpolation,
|
||||||
KeyedAccess,
|
KeyedAccess,
|
||||||
@ -68,7 +68,7 @@ export function main() {
|
|||||||
it('should support Pipe', () => {
|
it('should support Pipe', () => {
|
||||||
var originalExp = '(a | b)';
|
var originalExp = '(a | b)';
|
||||||
var ast = parseBinding(originalExp).ast;
|
var ast = parseBinding(originalExp).ast;
|
||||||
expect(ast).toBeAnInstanceOf(Pipe);
|
expect(ast).toBeAnInstanceOf(BindingPipe);
|
||||||
expect(unparser.unparse(ast)).toEqual(originalExp);
|
expect(unparser.unparse(ast)).toEqual(originalExp);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,45 +1,77 @@
|
|||||||
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
import {
|
||||||
|
ddescribe,
|
||||||
|
describe,
|
||||||
|
it,
|
||||||
|
iit,
|
||||||
|
xit,
|
||||||
|
expect,
|
||||||
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
SpyPipe,
|
||||||
|
SpyPipeFactory
|
||||||
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {PipeRegistry} from 'angular2/src/change_detection/pipes/pipe_registry';
|
import {PipeRegistry} from 'angular2/src/change_detection/pipes/pipe_registry';
|
||||||
import {Pipe} from 'angular2/src/change_detection/pipes/pipe';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe("pipe registry", () => {
|
describe("pipe registry", () => {
|
||||||
var firstPipe = new Pipe();
|
var firstPipe;
|
||||||
var secondPipe = new Pipe();
|
var secondPipe;
|
||||||
|
|
||||||
|
var firstPipeFactory;
|
||||||
|
var secondPipeFactory;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
firstPipe = <any>new SpyPipe();
|
||||||
|
secondPipe = <any>new SpyPipe();
|
||||||
|
|
||||||
|
firstPipeFactory = <any>new SpyPipeFactory();
|
||||||
|
secondPipeFactory = <any>new SpyPipeFactory();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return an existing pipe if it can support the passed in object", () => {
|
||||||
|
var r = new PipeRegistry({"type": []});
|
||||||
|
|
||||||
|
firstPipe.spy("supports").andReturn(true);
|
||||||
|
|
||||||
|
expect(r.get("type", "some object", null, firstPipe)).toEqual(firstPipe);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call onDestroy on the provided pipe if it cannot support the provided object",
|
||||||
|
() => {
|
||||||
|
firstPipe.spy("supports").andReturn(false);
|
||||||
|
firstPipeFactory.spy("supports").andReturn(true);
|
||||||
|
firstPipeFactory.spy("create").andReturn(secondPipe);
|
||||||
|
|
||||||
|
var r = new PipeRegistry({"type": [firstPipeFactory]});
|
||||||
|
|
||||||
|
expect(r.get("type", "some object", null, firstPipe)).toEqual(secondPipe);
|
||||||
|
expect(firstPipe.spy("onDestroy")).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it("should return the first pipe supporting the data type", () => {
|
it("should return the first pipe supporting the data type", () => {
|
||||||
var r = new PipeRegistry(
|
firstPipeFactory.spy("supports").andReturn(false);
|
||||||
{"type": [new PipeFactory(false, firstPipe), new PipeFactory(true, secondPipe)]});
|
firstPipeFactory.spy("create").andReturn(firstPipe);
|
||||||
|
|
||||||
expect(r.get("type", "some object", null)).toBe(secondPipe);
|
secondPipeFactory.spy("supports").andReturn(true);
|
||||||
|
secondPipeFactory.spy("create").andReturn(secondPipe);
|
||||||
|
|
||||||
|
var r = new PipeRegistry({"type": [firstPipeFactory, secondPipeFactory]});
|
||||||
|
|
||||||
|
expect(r.get("type", "some object")).toBe(secondPipe);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw when no matching type", () => {
|
it("should throw when no matching type", () => {
|
||||||
var r = new PipeRegistry({});
|
var r = new PipeRegistry({});
|
||||||
expect(() => r.get("unknown", "some object", null))
|
expect(() => r.get("unknown", "some object"))
|
||||||
.toThrowError(`Cannot find 'unknown' pipe supporting object 'some object'`);
|
.toThrowError(`Cannot find 'unknown' pipe supporting object 'some object'`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw when no matching pipe", () => {
|
it("should throw when no matching pipe", () => {
|
||||||
var r = new PipeRegistry({"type": []});
|
var r = new PipeRegistry({"type": []});
|
||||||
|
|
||||||
expect(() => r.get("type", "some object", null))
|
expect(() => r.get("type", "some object"))
|
||||||
.toThrowError(`Cannot find 'type' pipe supporting object 'some object'`);
|
.toThrowError(`Cannot find 'type' pipe supporting object 'some object'`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class PipeFactory {
|
|
||||||
shouldSupport: boolean;
|
|
||||||
pipe: any;
|
|
||||||
|
|
||||||
constructor(shouldSupport: boolean, pipe: any) {
|
|
||||||
this.shouldSupport = shouldSupport;
|
|
||||||
this.pipe = pipe;
|
|
||||||
}
|
|
||||||
|
|
||||||
supports(obj): boolean { return this.shouldSupport; }
|
|
||||||
|
|
||||||
create(cdRef): Pipe { return this.pipe; }
|
|
||||||
}
|
|
@ -36,6 +36,7 @@ import {PromiseWrapper, EventEmitter, ObservableWrapper} from 'angular2/src/faca
|
|||||||
|
|
||||||
import {Injector, bind, Injectable, Binding, forwardRef, OpaqueToken, Inject} from 'angular2/di';
|
import {Injector, bind, Injectable, Binding, forwardRef, OpaqueToken, Inject} from 'angular2/di';
|
||||||
import {
|
import {
|
||||||
|
PipeFactory,
|
||||||
PipeRegistry,
|
PipeRegistry,
|
||||||
defaultPipeRegistry,
|
defaultPipeRegistry,
|
||||||
ChangeDetection,
|
ChangeDetection,
|
||||||
@ -243,12 +244,11 @@ export function main() {
|
|||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should support pipes in bindings and bind config",
|
it("should support pipes in bindings",
|
||||||
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
|
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
|
||||||
tb.overrideView(MyComp, new viewAnn.View({
|
tb.overrideView(MyComp, new viewAnn.View({
|
||||||
template:
|
template: '<div [my-dir] #dir="mydir" [elprop]="ctxProp | double"></div>',
|
||||||
'<component-with-pipes #comp [prop]="ctxProp | double"></component-with-pipes>',
|
directives: [MyDir]
|
||||||
directives: [ComponentWithPipes]
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
tb.createView(MyComp, {context: ctx})
|
tb.createView(MyComp, {context: ctx})
|
||||||
@ -256,10 +256,8 @@ export function main() {
|
|||||||
ctx.ctxProp = 'a';
|
ctx.ctxProp = 'a';
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
var comp = view.rawView.locals.get("comp");
|
var dir = view.rawView.locals.get("dir");
|
||||||
|
expect(dir.dirProp).toEqual('aa');
|
||||||
// it is doubled twice: once in the binding, second time in the bind config
|
|
||||||
expect(comp.prop).toEqual('aaaa');
|
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
@ -1292,7 +1290,7 @@ class DynamicViewport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive({selector: '[my-dir]', properties: ['dirProp: elprop']})
|
@Directive({selector: '[my-dir]', properties: ['dirProp: elprop'], exportAs: 'mydir'})
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class MyDir {
|
class MyDir {
|
||||||
dirProp: string;
|
dirProp: string;
|
||||||
@ -1349,7 +1347,7 @@ class MyComp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({selector: 'component-with-pipes', properties: ["prop: prop | double"]})
|
@Component({selector: 'component-with-pipes', properties: ["prop"]})
|
||||||
@View({template: ''})
|
@View({template: ''})
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class ComponentWithPipes {
|
class ComponentWithPipes {
|
||||||
@ -1430,14 +1428,16 @@ class SomeViewport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class DoublePipe extends Pipe {
|
class DoublePipe implements Pipe {
|
||||||
|
onDestroy() {}
|
||||||
|
|
||||||
supports(obj) { return true; }
|
supports(obj) { return true; }
|
||||||
|
|
||||||
transform(value) { return `${value}${value}`; }
|
transform(value) { return `${value}${value}`; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class DoublePipeFactory {
|
class DoublePipeFactory implements PipeFactory {
|
||||||
supports(obj) { return true; }
|
supports(obj) { return true; }
|
||||||
|
|
||||||
create(cdRef) { return new DoublePipe(); }
|
create(cdRef) { return new DoublePipe(); }
|
||||||
|
@ -86,17 +86,6 @@ export function main() {
|
|||||||
expect(directiveBinding.propertyBindings.get('dirProp').source).toEqual('someExpr');
|
expect(directiveBinding.propertyBindings.get('dirProp').source).toEqual('someExpr');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should bind directive properties with pipes', () => {
|
|
||||||
var results = process(el('<div some-decor-props></div>'),
|
|
||||||
{'elProp': parser.parseBinding('someExpr', '')});
|
|
||||||
var directiveBinding = results[0].directives[0];
|
|
||||||
var pipedProp = <any>directiveBinding.propertyBindings.get('doubleProp');
|
|
||||||
var simpleProp = <any>directiveBinding.propertyBindings.get('dirProp');
|
|
||||||
expect(pipedProp.ast.name).toEqual('double');
|
|
||||||
expect(pipedProp.ast.exp).toEqual(simpleProp.ast);
|
|
||||||
expect(simpleProp.source).toEqual('someExpr');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should bind directive properties from attribute values', () => {
|
it('should bind directive properties from attribute values', () => {
|
||||||
var results = process(el('<div some-decor-props el-prop="someValue"></div>'));
|
var results = process(el('<div some-decor-props el-prop="someValue"></div>'));
|
||||||
var directiveBinding = results[0].directives[0];
|
var directiveBinding = results[0].directives[0];
|
||||||
@ -237,7 +226,7 @@ var decoratorWithMultipleAttrs = DirectiveMetadata.create({
|
|||||||
|
|
||||||
var someDirectiveWithProps = DirectiveMetadata.create({
|
var someDirectiveWithProps = DirectiveMetadata.create({
|
||||||
selector: '[some-decor-props]',
|
selector: '[some-decor-props]',
|
||||||
properties: ['dirProp: elProp', 'doubleProp: elProp | double'],
|
properties: ['dirProp: elProp'],
|
||||||
readAttributes: ['some-attr']
|
readAttributes: ['some-attr']
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user