feat(change_detection): add support for pipes in the template
This commit is contained in:
parent
29f5ee0c29
commit
987a5fdf56
|
@ -35,19 +35,33 @@ export var defaultPipes = {
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
var _registry = new PipeRegistry(defaultPipes);
|
|
||||||
|
|
||||||
export class DynamicChangeDetection extends ChangeDetection {
|
export class DynamicChangeDetection extends ChangeDetection {
|
||||||
|
registry:PipeRegistry;
|
||||||
|
|
||||||
|
constructor(registry:PipeRegistry) {
|
||||||
|
super();
|
||||||
|
this.registry = registry;
|
||||||
|
}
|
||||||
|
|
||||||
createProtoChangeDetector(name:string):ProtoChangeDetector{
|
createProtoChangeDetector(name:string):ProtoChangeDetector{
|
||||||
return new DynamicProtoChangeDetector(_registry);
|
return new DynamicProtoChangeDetector(this.registry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class JitChangeDetection extends ChangeDetection {
|
export class JitChangeDetection extends ChangeDetection {
|
||||||
|
registry:PipeRegistry;
|
||||||
|
|
||||||
|
constructor(registry:PipeRegistry) {
|
||||||
|
super();
|
||||||
|
this.registry = registry;
|
||||||
|
}
|
||||||
|
|
||||||
createProtoChangeDetector(name:string):ProtoChangeDetector{
|
createProtoChangeDetector(name:string):ProtoChangeDetector{
|
||||||
return new JitProtoChangeDetector(_registry);
|
return new JitProtoChangeDetector(this.registry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export var dynamicChangeDetection = new DynamicChangeDetection();
|
var _registry = new PipeRegistry(defaultPipes);
|
||||||
export var jitChangeDetection = new JitChangeDetection();
|
|
||||||
|
export var dynamicChangeDetection = new DynamicChangeDetection(_registry);
|
||||||
|
export var jitChangeDetection = new JitChangeDetection(_registry);
|
|
@ -14,7 +14,6 @@ import {
|
||||||
RECORD_TYPE_INVOKE_CLOSURE,
|
RECORD_TYPE_INVOKE_CLOSURE,
|
||||||
RECORD_TYPE_PRIMITIVE_OP,
|
RECORD_TYPE_PRIMITIVE_OP,
|
||||||
RECORD_TYPE_KEYED_ACCESS,
|
RECORD_TYPE_KEYED_ACCESS,
|
||||||
RECORD_TYPE_INVOKE_FORMATTER,
|
|
||||||
RECORD_TYPE_PIPE,
|
RECORD_TYPE_PIPE,
|
||||||
RECORD_TYPE_INTERPOLATE
|
RECORD_TYPE_INTERPOLATE
|
||||||
} from './proto_record';
|
} from './proto_record';
|
||||||
|
@ -26,10 +25,9 @@ import {
|
||||||
*
|
*
|
||||||
* For example: An expression `address.city` will result in the following class:
|
* For example: An expression `address.city` will result in the following class:
|
||||||
*
|
*
|
||||||
* var ChangeDetector0 = function ChangeDetector0(dispatcher, formatters, protos) {
|
* var ChangeDetector0 = function ChangeDetector0(dispatcher, protos) {
|
||||||
* AbstractChangeDetector.call(this);
|
* AbstractChangeDetector.call(this);
|
||||||
* this.dispatcher = dispatcher;
|
* this.dispatcher = dispatcher;
|
||||||
* this.formatters = formatters;
|
|
||||||
* this.protos = protos;
|
* this.protos = protos;
|
||||||
*
|
*
|
||||||
* this.context = null;
|
* this.context = null;
|
||||||
|
@ -89,12 +87,11 @@ import {
|
||||||
var ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
|
var ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
|
||||||
var UTIL = "ChangeDetectionUtil";
|
var UTIL = "ChangeDetectionUtil";
|
||||||
var DISPATCHER_ACCESSOR = "this.dispatcher";
|
var DISPATCHER_ACCESSOR = "this.dispatcher";
|
||||||
var FORMATTERS_ACCESSOR = "this.formatters";
|
var PIPE_REGISTRY_ACCESSOR = "this.pipeRegistry";
|
||||||
var PROTOS_ACCESSOR = "this.protos";
|
var PROTOS_ACCESSOR = "this.protos";
|
||||||
var CHANGE_LOCAL = "change";
|
var CHANGE_LOCAL = "change";
|
||||||
var CHANGES_LOCAL = "changes";
|
var CHANGES_LOCAL = "changes";
|
||||||
var TEMP_LOCAL = "temp";
|
var TEMP_LOCAL = "temp";
|
||||||
var PIPE_REGISTRY_ACCESSOR = "this.pipeRegistry";
|
|
||||||
|
|
||||||
function typeTemplate(type:string, cons:string, detectChanges:string, setContext:string):string {
|
function typeTemplate(type:string, cons:string, detectChanges:string, setContext:string):string {
|
||||||
return `
|
return `
|
||||||
|
@ -102,18 +99,17 @@ ${cons}
|
||||||
${detectChanges}
|
${detectChanges}
|
||||||
${setContext};
|
${setContext};
|
||||||
|
|
||||||
return function(dispatcher, formatters, pipeRegistry) {
|
return function(dispatcher, pipeRegistry) {
|
||||||
return new ${type}(dispatcher, formatters, pipeRegistry, protos);
|
return new ${type}(dispatcher, pipeRegistry, protos);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function constructorTemplate(type:string, fieldsDefinitions:string):string {
|
function constructorTemplate(type:string, fieldsDefinitions:string):string {
|
||||||
return `
|
return `
|
||||||
var ${type} = function ${type}(dispatcher, formatters, pipeRegistry, protos) {
|
var ${type} = function ${type}(dispatcher, pipeRegistry, protos) {
|
||||||
${ABSTRACT_CHANGE_DETECTOR}.call(this);
|
${ABSTRACT_CHANGE_DETECTOR}.call(this);
|
||||||
${DISPATCHER_ACCESSOR} = dispatcher;
|
${DISPATCHER_ACCESSOR} = dispatcher;
|
||||||
${FORMATTERS_ACCESSOR} = formatters;
|
|
||||||
${PIPE_REGISTRY_ACCESSOR} = pipeRegistry;
|
${PIPE_REGISTRY_ACCESSOR} = pipeRegistry;
|
||||||
${PROTOS_ACCESSOR} = protos;
|
${PROTOS_ACCESSOR} = protos;
|
||||||
${fieldsDefinitions}
|
${fieldsDefinitions}
|
||||||
|
@ -381,9 +377,6 @@ export class ChangeDetectorJITGenerator {
|
||||||
case RECORD_TYPE_INTERPOLATE:
|
case RECORD_TYPE_INTERPOLATE:
|
||||||
return assignmentTemplate(newValue, this.genInterpolation(r));
|
return assignmentTemplate(newValue, this.genInterpolation(r));
|
||||||
|
|
||||||
case RECORD_TYPE_INVOKE_FORMATTER:
|
|
||||||
return assignmentTemplate(newValue, `${FORMATTERS_ACCESSOR}.get("${r.name}")(${args})`);
|
|
||||||
|
|
||||||
case RECORD_TYPE_KEYED_ACCESS:
|
case RECORD_TYPE_KEYED_ACCESS:
|
||||||
var key = this.localNames[r.args[0]];
|
var key = this.localNames[r.args[0]];
|
||||||
return assignmentTemplate(newValue, `${context}[${key}]`);
|
return assignmentTemplate(newValue, `${context}[${key}]`);
|
||||||
|
|
|
@ -16,7 +16,6 @@ import {
|
||||||
RECORD_TYPE_INVOKE_CLOSURE,
|
RECORD_TYPE_INVOKE_CLOSURE,
|
||||||
RECORD_TYPE_PRIMITIVE_OP,
|
RECORD_TYPE_PRIMITIVE_OP,
|
||||||
RECORD_TYPE_KEYED_ACCESS,
|
RECORD_TYPE_KEYED_ACCESS,
|
||||||
RECORD_TYPE_INVOKE_FORMATTER,
|
|
||||||
RECORD_TYPE_PIPE,
|
RECORD_TYPE_PIPE,
|
||||||
RECORD_TYPE_INTERPOLATE
|
RECORD_TYPE_INTERPOLATE
|
||||||
} from './proto_record';
|
} from './proto_record';
|
||||||
|
@ -25,7 +24,6 @@ import {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} from './ex
|
||||||
|
|
||||||
export class DynamicChangeDetector extends AbstractChangeDetector {
|
export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
dispatcher:any;
|
dispatcher:any;
|
||||||
formatters:Map;
|
|
||||||
pipeRegistry;
|
pipeRegistry;
|
||||||
|
|
||||||
values:List;
|
values:List;
|
||||||
|
@ -35,10 +33,9 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
|
|
||||||
protos:List<ProtoRecord>;
|
protos:List<ProtoRecord>;
|
||||||
|
|
||||||
constructor(dispatcher:any, formatters:Map, pipeRegistry:PipeRegistry, protoRecords:List<ProtoRecord>) {
|
constructor(dispatcher:any, pipeRegistry:PipeRegistry, protoRecords:List<ProtoRecord>) {
|
||||||
super();
|
super();
|
||||||
this.dispatcher = dispatcher;
|
this.dispatcher = dispatcher;
|
||||||
this.formatters = formatters;
|
|
||||||
this.pipeRegistry = pipeRegistry;
|
this.pipeRegistry = pipeRegistry;
|
||||||
|
|
||||||
this.values = ListWrapper.createFixedSize(protoRecords.length + 1);
|
this.values = ListWrapper.createFixedSize(protoRecords.length + 1);
|
||||||
|
@ -149,10 +146,6 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
case RECORD_TYPE_PRIMITIVE_OP:
|
case RECORD_TYPE_PRIMITIVE_OP:
|
||||||
return FunctionWrapper.apply(proto.funcOrValue, this._readArgs(proto));
|
return FunctionWrapper.apply(proto.funcOrValue, this._readArgs(proto));
|
||||||
|
|
||||||
case RECORD_TYPE_INVOKE_FORMATTER:
|
|
||||||
var formatter = MapWrapper.get(this.formatters, proto.funcOrValue);
|
|
||||||
return FunctionWrapper.apply(formatter, this._readArgs(proto));
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new BaseException(`Unknown operation ${proto.mode}`);
|
throw new BaseException(`Unknown operation ${proto.mode}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,31 +170,15 @@ export class KeyedAccess extends AST {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Formatter extends AST {
|
export class Pipe extends AST {
|
||||||
exp:AST;
|
exp:AST;
|
||||||
name:string;
|
name:string;
|
||||||
args:List<AST>;
|
args:List<AST>;
|
||||||
allArgs:List<AST>;
|
|
||||||
constructor(exp:AST, name:string, args:List) {
|
constructor(exp:AST, name:string, args:List) {
|
||||||
super();
|
super();
|
||||||
this.exp = exp;
|
this.exp = exp;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.allArgs = ListWrapper.concat([exp], args);
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
return visitor.visitFormatter(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Pipe extends AST {
|
|
||||||
exp:AST;
|
|
||||||
name:string;
|
|
||||||
constructor(exp:AST, name:string) {
|
|
||||||
super();
|
|
||||||
this.exp = exp;
|
|
||||||
this.name = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visit(visitor) {
|
visit(visitor) {
|
||||||
|
@ -459,7 +443,6 @@ export class AstVisitor {
|
||||||
visitBinary(ast:Binary) {}
|
visitBinary(ast:Binary) {}
|
||||||
visitChain(ast:Chain){}
|
visitChain(ast:Chain){}
|
||||||
visitConditional(ast:Conditional) {}
|
visitConditional(ast:Conditional) {}
|
||||||
visitFormatter(ast:Formatter) {}
|
|
||||||
visitPipe(ast:Pipe) {}
|
visitPipe(ast:Pipe) {}
|
||||||
visitFunctionCall(ast:FunctionCall) {}
|
visitFunctionCall(ast:FunctionCall) {}
|
||||||
visitImplicitReceiver(ast:ImplicitReceiver) {}
|
visitImplicitReceiver(ast:ImplicitReceiver) {}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import {
|
||||||
Binary,
|
Binary,
|
||||||
PrefixNot,
|
PrefixNot,
|
||||||
Conditional,
|
Conditional,
|
||||||
Formatter,
|
|
||||||
Pipe,
|
Pipe,
|
||||||
Assignment,
|
Assignment,
|
||||||
Chain,
|
Chain,
|
||||||
|
@ -57,7 +56,7 @@ export class Parser {
|
||||||
if (ListWrapper.isEmpty(pipes)) return bindingAst;
|
if (ListWrapper.isEmpty(pipes)) return bindingAst;
|
||||||
|
|
||||||
var res = ListWrapper.reduce(pipes,
|
var res = ListWrapper.reduce(pipes,
|
||||||
(result, currentPipeName) => new Pipe(result, currentPipeName),
|
(result, currentPipeName) => new Pipe(result, currentPipeName, []),
|
||||||
bindingAst.ast);
|
bindingAst.ast);
|
||||||
return new ASTWithSource(res, bindingAst.source, bindingAst.location);
|
return new ASTWithSource(res, bindingAst.source, bindingAst.location);
|
||||||
}
|
}
|
||||||
|
@ -191,7 +190,7 @@ class _ParseAST {
|
||||||
parseChain():AST {
|
parseChain():AST {
|
||||||
var exprs = [];
|
var exprs = [];
|
||||||
while (this.index < this.tokens.length) {
|
while (this.index < this.tokens.length) {
|
||||||
var expr = this.parseFormatter();
|
var expr = this.parsePipe();
|
||||||
ListWrapper.push(exprs, expr);
|
ListWrapper.push(exprs, expr);
|
||||||
|
|
||||||
if (this.optionalCharacter($SEMICOLON)) {
|
if (this.optionalCharacter($SEMICOLON)) {
|
||||||
|
@ -208,18 +207,18 @@ class _ParseAST {
|
||||||
return new Chain(exprs);
|
return new Chain(exprs);
|
||||||
}
|
}
|
||||||
|
|
||||||
parseFormatter() {
|
parsePipe() {
|
||||||
var result = this.parseExpression();
|
var result = this.parseExpression();
|
||||||
while (this.optionalOperator("|")) {
|
while (this.optionalOperator("|")) {
|
||||||
if (this.parseAction) {
|
if (this.parseAction) {
|
||||||
this.error("Cannot have a formatter in an action expression");
|
this.error("Cannot have a pipe in an action expression");
|
||||||
}
|
}
|
||||||
var name = this.expectIdentifierOrKeyword();
|
var name = this.expectIdentifierOrKeyword();
|
||||||
var args = ListWrapper.create();
|
var args = ListWrapper.create();
|
||||||
while (this.optionalCharacter($COLON)) {
|
while (this.optionalCharacter($COLON)) {
|
||||||
ListWrapper.push(args, this.parseExpression());
|
ListWrapper.push(args, this.parseExpression());
|
||||||
}
|
}
|
||||||
result = new Formatter(result, name, args);
|
result = new Pipe(result, name, args);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -380,7 +379,7 @@ class _ParseAST {
|
||||||
|
|
||||||
parsePrimary() {
|
parsePrimary() {
|
||||||
if (this.optionalCharacter($LPAREN)) {
|
if (this.optionalCharacter($LPAREN)) {
|
||||||
var result = this.parseFormatter();
|
var result = this.parsePipe();
|
||||||
this.expectCharacter($RPAREN);
|
this.expectCharacter($RPAREN);
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import {
|
||||||
Binary,
|
Binary,
|
||||||
Chain,
|
Chain,
|
||||||
Conditional,
|
Conditional,
|
||||||
Formatter,
|
|
||||||
Pipe,
|
Pipe,
|
||||||
FunctionCall,
|
FunctionCall,
|
||||||
ImplicitReceiver,
|
ImplicitReceiver,
|
||||||
|
@ -40,14 +39,13 @@ import {
|
||||||
RECORD_TYPE_INVOKE_CLOSURE,
|
RECORD_TYPE_INVOKE_CLOSURE,
|
||||||
RECORD_TYPE_PRIMITIVE_OP,
|
RECORD_TYPE_PRIMITIVE_OP,
|
||||||
RECORD_TYPE_KEYED_ACCESS,
|
RECORD_TYPE_KEYED_ACCESS,
|
||||||
RECORD_TYPE_INVOKE_FORMATTER,
|
|
||||||
RECORD_TYPE_PIPE,
|
RECORD_TYPE_PIPE,
|
||||||
RECORD_TYPE_INTERPOLATE
|
RECORD_TYPE_INTERPOLATE
|
||||||
} from './proto_record';
|
} from './proto_record';
|
||||||
|
|
||||||
export class ProtoChangeDetector {
|
export class ProtoChangeDetector {
|
||||||
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null){}
|
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null){}
|
||||||
instantiate(dispatcher:any, formatters:Map):ChangeDetector{
|
instantiate(dispatcher:any):ChangeDetector{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,10 +66,9 @@ export class DynamicProtoChangeDetector extends ProtoChangeDetector {
|
||||||
this._recordBuilder.addAst(ast, bindingMemento, directiveMemento);
|
this._recordBuilder.addAst(ast, bindingMemento, directiveMemento);
|
||||||
}
|
}
|
||||||
|
|
||||||
instantiate(dispatcher:any, formatters:Map) {
|
instantiate(dispatcher:any) {
|
||||||
this._createRecordsIfNecessary();
|
this._createRecordsIfNecessary();
|
||||||
return new DynamicChangeDetector(dispatcher, formatters,
|
return new DynamicChangeDetector(dispatcher, this._pipeRegistry, this._records);
|
||||||
this._pipeRegistry, this._records);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_createRecordsIfNecessary() {
|
_createRecordsIfNecessary() {
|
||||||
|
@ -99,9 +96,9 @@ export class JitProtoChangeDetector extends ProtoChangeDetector {
|
||||||
this._recordBuilder.addAst(ast, bindingMemento, directiveMemento);
|
this._recordBuilder.addAst(ast, bindingMemento, directiveMemento);
|
||||||
}
|
}
|
||||||
|
|
||||||
instantiate(dispatcher:any, formatters:Map) {
|
instantiate(dispatcher:any) {
|
||||||
this._createFactoryIfNecessary();
|
this._createFactoryIfNecessary();
|
||||||
return this._factory(dispatcher, formatters, this._pipeRegistry);
|
return this._factory(dispatcher, this._pipeRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
_createFactoryIfNecessary() {
|
_createFactoryIfNecessary() {
|
||||||
|
@ -178,10 +175,6 @@ class _ConvertAstIntoProtoRecords {
|
||||||
return this._addRecord(RECORD_TYPE_PROPERTY, ast.name, ast.getter, [], null, receiver);
|
return this._addRecord(RECORD_TYPE_PROPERTY, ast.name, ast.getter, [], null, receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitFormatter(ast:Formatter) {
|
|
||||||
return this._addRecord(RECORD_TYPE_INVOKE_FORMATTER, ast.name, ast.name, this._visitAll(ast.allArgs), null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitMethodCall(ast:MethodCall) {
|
visitMethodCall(ast:MethodCall) {
|
||||||
var receiver = ast.receiver.visit(this);
|
var receiver = ast.receiver.visit(this);
|
||||||
var args = this._visitAll(ast.args);
|
var args = this._visitAll(ast.args);
|
||||||
|
|
|
@ -7,7 +7,6 @@ export const RECORD_TYPE_PROPERTY = 3;
|
||||||
export const RECORD_TYPE_INVOKE_METHOD = 4;
|
export const RECORD_TYPE_INVOKE_METHOD = 4;
|
||||||
export const RECORD_TYPE_INVOKE_CLOSURE = 5;
|
export const RECORD_TYPE_INVOKE_CLOSURE = 5;
|
||||||
export const RECORD_TYPE_KEYED_ACCESS = 6;
|
export const RECORD_TYPE_KEYED_ACCESS = 6;
|
||||||
export const RECORD_TYPE_INVOKE_FORMATTER = 7;
|
|
||||||
export const RECORD_TYPE_PIPE = 8;
|
export const RECORD_TYPE_PIPE = 8;
|
||||||
export const RECORD_TYPE_INTERPOLATE = 9;
|
export const RECORD_TYPE_INTERPOLATE = 9;
|
||||||
|
|
||||||
|
@ -54,7 +53,6 @@ export class ProtoRecord {
|
||||||
|
|
||||||
isPureFunction():boolean {
|
isPureFunction():boolean {
|
||||||
return this.mode === RECORD_TYPE_INTERPOLATE ||
|
return this.mode === RECORD_TYPE_INTERPOLATE ||
|
||||||
this.mode === RECORD_TYPE_INVOKE_FORMATTER ||
|
|
||||||
this.mode === RECORD_TYPE_PRIMITIVE_OP;
|
this.mode === RECORD_TYPE_PRIMITIVE_OP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,6 @@ import {EventManager} from 'angular2/src/core/events/event_manager';
|
||||||
|
|
||||||
const NG_BINDING_CLASS = 'ng-binding';
|
const NG_BINDING_CLASS = 'ng-binding';
|
||||||
const NG_BINDING_CLASS_SELECTOR = '.ng-binding';
|
const NG_BINDING_CLASS_SELECTOR = '.ng-binding';
|
||||||
// TODO(tbosch): Cannot use `const` because of Dart.
|
|
||||||
var NO_FORMATTERS = MapWrapper.create();
|
|
||||||
|
|
||||||
// TODO(rado): make this configurable/smarter.
|
// TODO(rado): make this configurable/smarter.
|
||||||
var VIEW_POOL_CAPACITY = 10000;
|
var VIEW_POOL_CAPACITY = 10000;
|
||||||
|
@ -50,7 +48,7 @@ export class View {
|
||||||
constructor(proto:ProtoView, nodes:List<Node>, protoChangeDetector:ProtoChangeDetector, protoContextLocals:Map) {
|
constructor(proto:ProtoView, nodes:List<Node>, protoChangeDetector:ProtoChangeDetector, protoContextLocals:Map) {
|
||||||
this.proto = proto;
|
this.proto = proto;
|
||||||
this.nodes = nodes;
|
this.nodes = nodes;
|
||||||
this.changeDetector = protoChangeDetector.instantiate(this, NO_FORMATTERS);
|
this.changeDetector = protoChangeDetector.instantiate(this);
|
||||||
this.elementInjectors = null;
|
this.elementInjectors = null;
|
||||||
this.rootElementInjectors = null;
|
this.rootElementInjectors = null;
|
||||||
this.textNodes = null;
|
this.textNodes = null;
|
||||||
|
|
|
@ -3,7 +3,6 @@ import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, IS_DAR
|
||||||
import {isPresent, isBlank, isJsObject, BaseException, FunctionWrapper} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank, isJsObject, BaseException, FunctionWrapper} from 'angular2/src/facade/lang';
|
||||||
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
import {Pipe} from 'angular2/src/change_detection/parser/ast';
|
|
||||||
import {Parser} from 'angular2/src/change_detection/parser/parser';
|
import {Parser} from 'angular2/src/change_detection/parser/parser';
|
||||||
import {Lexer} from 'angular2/src/change_detection/parser/lexer';
|
import {Lexer} from 'angular2/src/change_detection/parser/lexer';
|
||||||
|
|
||||||
|
@ -29,24 +28,18 @@ export function main() {
|
||||||
return parser.parseBinding(exp, location);
|
return parser.parseBinding(exp, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createChangeDetector(memo:string, exp:string, context = null, formatters = null,
|
function createChangeDetector(memo:string, exp:string, context = null, registry = null) {
|
||||||
registry = null, pipeType:string = null) {
|
|
||||||
var pcd = createProtoChangeDetector(registry);
|
var pcd = createProtoChangeDetector(registry);
|
||||||
var parsedAst = ast(exp);
|
pcd.addAst(ast(exp), memo, memo);
|
||||||
if (isPresent(pipeType)) {
|
|
||||||
parsedAst = new Pipe(parsedAst, pipeType);
|
|
||||||
}
|
|
||||||
pcd.addAst(parsedAst, memo, memo);
|
|
||||||
|
|
||||||
var dispatcher = new TestDispatcher();
|
var dispatcher = new TestDispatcher();
|
||||||
var cd = pcd.instantiate(dispatcher, formatters);
|
var cd = pcd.instantiate(dispatcher);
|
||||||
cd.setContext(context);
|
cd.setContext(context);
|
||||||
|
|
||||||
return {"changeDetector" : cd, "dispatcher" : dispatcher};
|
return {"changeDetector" : cd, "dispatcher" : dispatcher};
|
||||||
}
|
}
|
||||||
|
|
||||||
function executeWatch(memo:string, exp:string, context = null, formatters = null) {
|
function executeWatch(memo:string, exp:string, context = null) {
|
||||||
var res = createChangeDetector(memo, exp, context, formatters);
|
var res = createChangeDetector(memo, exp, context);
|
||||||
res["changeDetector"].detectChanges();
|
res["changeDetector"].detectChanges();
|
||||||
return res["dispatcher"].log;
|
return res["dispatcher"].log;
|
||||||
}
|
}
|
||||||
|
@ -182,14 +175,6 @@ export function main() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should support formatters", () => {
|
|
||||||
var formatters = MapWrapper.createFromPairs([
|
|
||||||
['uppercase', (v) => v.toUpperCase()],
|
|
||||||
['wrap', (v, before, after) => `${before}${v}${after}`]]);
|
|
||||||
expect(executeWatch('str', '"aBc" | uppercase', null, formatters)).toEqual(['str=ABC']);
|
|
||||||
expect(executeWatch('str', '"b" | wrap:"a":"c"', null, formatters)).toEqual(['str=abc']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should support interpolation", () => {
|
it("should support interpolation", () => {
|
||||||
var parser = new Parser(new Lexer());
|
var parser = new Parser(new Lexer());
|
||||||
var pcd = createProtoChangeDetector();
|
var pcd = createProtoChangeDetector();
|
||||||
|
@ -197,7 +182,7 @@ export function main() {
|
||||||
pcd.addAst(ast, "memo", "memo");
|
pcd.addAst(ast, "memo", "memo");
|
||||||
|
|
||||||
var dispatcher = new TestDispatcher();
|
var dispatcher = new TestDispatcher();
|
||||||
var cd = pcd.instantiate(dispatcher, MapWrapper.create());
|
var cd = pcd.instantiate(dispatcher);
|
||||||
cd.setContext(new TestData("value"));
|
cd.setContext(new TestData("value"));
|
||||||
|
|
||||||
cd.detectChanges();
|
cd.detectChanges();
|
||||||
|
@ -213,7 +198,7 @@ export function main() {
|
||||||
pcd.addAst(ast("100 + 200"), "memo2", "2");
|
pcd.addAst(ast("100 + 200"), "memo2", "2");
|
||||||
|
|
||||||
var dispatcher = new TestDispatcher();
|
var dispatcher = new TestDispatcher();
|
||||||
var cd = pcd.instantiate(dispatcher, null);
|
var cd = pcd.instantiate(dispatcher);
|
||||||
|
|
||||||
cd.detectChanges();
|
cd.detectChanges();
|
||||||
|
|
||||||
|
@ -227,7 +212,7 @@ export function main() {
|
||||||
pcd.addAst(ast("c()"), "c", "2");
|
pcd.addAst(ast("c()"), "c", "2");
|
||||||
|
|
||||||
var dispatcher = new TestDispatcher();
|
var dispatcher = new TestDispatcher();
|
||||||
var cd = pcd.instantiate(dispatcher, null);
|
var cd = pcd.instantiate(dispatcher);
|
||||||
|
|
||||||
var tr = new TestRecord();
|
var tr = new TestRecord();
|
||||||
tr.a = () => {
|
tr.a = () => {
|
||||||
|
@ -256,7 +241,7 @@ export function main() {
|
||||||
pcd.addAst(ast("a"), "a", 1);
|
pcd.addAst(ast("a"), "a", 1);
|
||||||
|
|
||||||
var dispatcher = new TestDispatcher();
|
var dispatcher = new TestDispatcher();
|
||||||
var cd = pcd.instantiate(dispatcher, null);
|
var cd = pcd.instantiate(dispatcher);
|
||||||
cd.setContext(new TestData('value'));
|
cd.setContext(new TestData('value'));
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
|
@ -271,7 +256,7 @@ export function main() {
|
||||||
var pcd = createProtoChangeDetector();
|
var pcd = createProtoChangeDetector();
|
||||||
pcd.addAst(ast('invalidProp', 'someComponent'), "a", 1);
|
pcd.addAst(ast('invalidProp', 'someComponent'), "a", 1);
|
||||||
|
|
||||||
var cd = pcd.instantiate(new TestDispatcher(), null);
|
var cd = pcd.instantiate(new TestDispatcher());
|
||||||
cd.setContext(null);
|
cd.setContext(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -318,10 +303,10 @@ export function main() {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
var protoParent = createProtoChangeDetector();
|
var protoParent = createProtoChangeDetector();
|
||||||
parent = protoParent.instantiate(null, null);
|
parent = protoParent.instantiate(null);
|
||||||
|
|
||||||
var protoChild = createProtoChangeDetector();
|
var protoChild = createProtoChangeDetector();
|
||||||
child = protoChild.instantiate(null, null);
|
child = protoChild.instantiate(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should add children", () => {
|
it("should add children", () => {
|
||||||
|
@ -340,25 +325,6 @@ export function main() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("optimizations", () => {
|
|
||||||
it("should not rerun formatters when args did not change", () => {
|
|
||||||
var count = 0;
|
|
||||||
var formatters = MapWrapper.createFromPairs([
|
|
||||||
['count', (v) => {count ++; "value"}]]);
|
|
||||||
|
|
||||||
var c = createChangeDetector('a', 'a | count', new TestData(null), formatters);
|
|
||||||
var cd = c["changeDetector"];
|
|
||||||
|
|
||||||
cd.detectChanges();
|
|
||||||
|
|
||||||
expect(count).toEqual(1);
|
|
||||||
|
|
||||||
cd.detectChanges();
|
|
||||||
|
|
||||||
expect(count).toEqual(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("mode", () => {
|
describe("mode", () => {
|
||||||
it("should not check a detached change detector", () => {
|
it("should not check a detached change detector", () => {
|
||||||
var c = createChangeDetector('name', 'a', new TestData("value"));
|
var c = createChangeDetector('name', 'a', new TestData("value"));
|
||||||
|
@ -383,7 +349,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should change CHECK_ONCE to CHECKED", () => {
|
it("should change CHECK_ONCE to CHECKED", () => {
|
||||||
var cd = createProtoChangeDetector().instantiate(null, null);
|
var cd = createProtoChangeDetector().instantiate(null);
|
||||||
cd.mode = CHECK_ONCE;
|
cd.mode = CHECK_ONCE;
|
||||||
|
|
||||||
cd.detectChanges();
|
cd.detectChanges();
|
||||||
|
@ -392,7 +358,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not change the CHECK_ALWAYS", () => {
|
it("should not change the CHECK_ALWAYS", () => {
|
||||||
var cd = createProtoChangeDetector().instantiate(null, null);
|
var cd = createProtoChangeDetector().instantiate(null);
|
||||||
cd.mode = CHECK_ALWAYS;
|
cd.mode = CHECK_ALWAYS;
|
||||||
|
|
||||||
cd.detectChanges();
|
cd.detectChanges();
|
||||||
|
@ -403,7 +369,7 @@ export function main() {
|
||||||
|
|
||||||
describe("markPathToRootAsCheckOnce", () => {
|
describe("markPathToRootAsCheckOnce", () => {
|
||||||
function changeDetector(mode, parent) {
|
function changeDetector(mode, parent) {
|
||||||
var cd = createProtoChangeDetector().instantiate(null, null);
|
var cd = createProtoChangeDetector().instantiate(null);
|
||||||
cd.mode = mode;
|
cd.mode = mode;
|
||||||
if (isPresent(parent)) parent.addChild(cd);
|
if (isPresent(parent)) parent.addChild(cd);
|
||||||
return cd;
|
return cd;
|
||||||
|
@ -435,7 +401,7 @@ export function main() {
|
||||||
var registry = new FakePipeRegistry('pipe', () => new CountingPipe());
|
var registry = new FakePipeRegistry('pipe', () => new CountingPipe());
|
||||||
var ctx = new Person("Megatron");
|
var ctx = new Person("Megatron");
|
||||||
|
|
||||||
var c = createChangeDetector("memo", "name", ctx, null, registry, 'pipe');
|
var c = createChangeDetector("memo", "name | pipe", ctx, registry);
|
||||||
var cd = c["changeDetector"];
|
var cd = c["changeDetector"];
|
||||||
var dispatcher = c["dispatcher"];
|
var dispatcher = c["dispatcher"];
|
||||||
|
|
||||||
|
@ -453,7 +419,7 @@ export function main() {
|
||||||
var registry = new FakePipeRegistry('pipe', () => new OncePipe());
|
var registry = new FakePipeRegistry('pipe', () => new OncePipe());
|
||||||
var ctx = new Person("Megatron");
|
var ctx = new Person("Megatron");
|
||||||
|
|
||||||
var c = createChangeDetector("memo", "name", ctx, null, registry, 'pipe');
|
var c = createChangeDetector("memo", "name | pipe", ctx, registry);
|
||||||
var cd = c["changeDetector"];
|
var cd = c["changeDetector"];
|
||||||
|
|
||||||
cd.detectChanges();
|
cd.detectChanges();
|
||||||
|
@ -471,7 +437,7 @@ export function main() {
|
||||||
var registry = new FakePipeRegistry('pipe', () => new IdentityPipe())
|
var registry = new FakePipeRegistry('pipe', () => new IdentityPipe())
|
||||||
var ctx = new Person("Megatron");
|
var ctx = new Person("Megatron");
|
||||||
|
|
||||||
var c = createChangeDetector("memo", "name", ctx, null, registry, 'pipe');
|
var c = createChangeDetector("memo", "name | pipe", ctx, registry);
|
||||||
var cd = c["changeDetector"];
|
var cd = c["changeDetector"];
|
||||||
var dispatcher = c["dispatcher"];
|
var dispatcher = c["dispatcher"];
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {Parser} from 'angular2/src/change_detection/parser/parser';
|
import {Parser} from 'angular2/src/change_detection/parser/parser';
|
||||||
import {Lexer} from 'angular2/src/change_detection/parser/lexer';
|
import {Lexer} from 'angular2/src/change_detection/parser/lexer';
|
||||||
import {ContextWithVariableBindings} from 'angular2/src/change_detection/parser/context_with_variable_bindings';
|
import {ContextWithVariableBindings} from 'angular2/src/change_detection/parser/context_with_variable_bindings';
|
||||||
import {Formatter, LiteralPrimitive} from 'angular2/src/change_detection/parser/ast';
|
import {Pipe, LiteralPrimitive} from 'angular2/src/change_detection/parser/ast';
|
||||||
|
|
||||||
class TestData {
|
class TestData {
|
||||||
a;
|
a;
|
||||||
|
@ -340,8 +340,8 @@ export function main() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should error when using formatters", () => {
|
it("should error when using pipes", () => {
|
||||||
expectEvalError('x|blah').toThrowError(new RegExp('Cannot have a formatter'));
|
expectEvalError('x|blah').toThrowError(new RegExp('Cannot have a pipe'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should pass exceptions', () => {
|
it('should pass exceptions', () => {
|
||||||
|
@ -367,16 +367,16 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("parseBinding", () => {
|
describe("parseBinding", () => {
|
||||||
describe("formatters", () => {
|
describe("pipes", () => {
|
||||||
it("should parse formatters", () => {
|
it("should parse pipes", () => {
|
||||||
var exp = parseBinding("'Foo'|uppercase").ast;
|
var exp = parseBinding("'Foo'|uppercase").ast;
|
||||||
expect(exp).toBeAnInstanceOf(Formatter);
|
expect(exp).toBeAnInstanceOf(Pipe);
|
||||||
expect(exp.name).toEqual("uppercase");
|
expect(exp.name).toEqual("uppercase");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should parse formatters with args", () => {
|
it("should parse pipes with args", () => {
|
||||||
var exp = parseBinding("1|increment:2").ast;
|
var exp = parseBinding("1|increment:2").ast;
|
||||||
expect(exp).toBeAnInstanceOf(Formatter);
|
expect(exp).toBeAnInstanceOf(Pipe);
|
||||||
expect(exp.name).toEqual("increment");
|
expect(exp.name).toEqual("increment");
|
||||||
expect(exp.args[0]).toBeAnInstanceOf(LiteralPrimitive);
|
expect(exp.args[0]).toBeAnInstanceOf(LiteralPrimitive);
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,8 @@ import {Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {Type, isPresent} from 'angular2/src/facade/lang';
|
import {Type, isPresent} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
import {Injector} from 'angular2/di';
|
import {Injector} from 'angular2/di';
|
||||||
import {Lexer, Parser, ChangeDetector, dynamicChangeDetection} from 'angular2/change_detection';
|
import {Lexer, Parser, ChangeDetector, dynamicChangeDetection,
|
||||||
|
DynamicChangeDetection, Pipe, PipeRegistry} from 'angular2/change_detection';
|
||||||
|
|
||||||
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
||||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||||
|
@ -24,9 +25,8 @@ export function main() {
|
||||||
describe('integration tests', function() {
|
describe('integration tests', function() {
|
||||||
var compiler, tplResolver;
|
var compiler, tplResolver;
|
||||||
|
|
||||||
beforeEach( () => {
|
function createCompiler(tplResolver, changedDetection) {
|
||||||
tplResolver = new FakeTemplateResolver();
|
return new Compiler(changedDetection,
|
||||||
compiler = new Compiler(dynamicChangeDetection,
|
|
||||||
new TemplateLoader(null),
|
new TemplateLoader(null),
|
||||||
new DirectiveMetadataReader(),
|
new DirectiveMetadataReader(),
|
||||||
new Parser(new Lexer()),
|
new Parser(new Lexer()),
|
||||||
|
@ -34,6 +34,11 @@ export function main() {
|
||||||
new NativeShadowDomStrategy(),
|
new NativeShadowDomStrategy(),
|
||||||
tplResolver
|
tplResolver
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach( () => {
|
||||||
|
tplResolver = new FakeTemplateResolver();
|
||||||
|
compiler = createCompiler(tplResolver, dynamicChangeDetection);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('react to record changes', function() {
|
describe('react to record changes', function() {
|
||||||
|
@ -114,6 +119,33 @@ export function main() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should support pipes in bindings and bind config", (done) => {
|
||||||
|
tplResolver.setTemplate(MyComp,
|
||||||
|
new Template({
|
||||||
|
inline: '<component-with-pipes #comp [prop]="ctxProp | double"></component-with-pipes>',
|
||||||
|
directives: [ComponentWithPipes]
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
var registry = new PipeRegistry({
|
||||||
|
"double" : [new DoublePipeFactory()]
|
||||||
|
});
|
||||||
|
var changeDetection = new DynamicChangeDetection(registry);
|
||||||
|
var compiler = createCompiler(tplResolver, changeDetection);
|
||||||
|
compiler.compile(MyComp).then((pv) => {
|
||||||
|
createView(pv);
|
||||||
|
|
||||||
|
ctx.ctxProp = 'a';
|
||||||
|
cd.detectChanges();
|
||||||
|
|
||||||
|
var comp = view.contextWithLocals.get("comp");
|
||||||
|
|
||||||
|
// it is doubled twice: once in the binding, second time in the bind config
|
||||||
|
expect(comp.prop).toEqual('aaaa');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should support nested components.', (done) => {
|
it('should support nested components.', (done) => {
|
||||||
tplResolver.setTemplate(MyComp, new Template({
|
tplResolver.setTemplate(MyComp, new Template({
|
||||||
inline: '<child-cmp></child-cmp>',
|
inline: '<child-cmp></child-cmp>',
|
||||||
|
@ -379,6 +411,20 @@ class MyComp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'component-with-pipes',
|
||||||
|
bind: {
|
||||||
|
"prop": "prop | double"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
@Template({
|
||||||
|
inline: ''
|
||||||
|
})
|
||||||
|
class ComponentWithPipes {
|
||||||
|
prop:string;
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'child-cmp',
|
selector: 'child-cmp',
|
||||||
componentServices: [MyService]
|
componentServices: [MyService]
|
||||||
|
@ -468,3 +514,24 @@ class FakeTemplateResolver extends TemplateResolver {
|
||||||
return super.resolve(component);
|
return super.resolve(component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DoublePipe extends Pipe {
|
||||||
|
supports(obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
transform(value) {
|
||||||
|
return `${value}${value}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DoublePipeFactory {
|
||||||
|
supports(obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
create() {
|
||||||
|
return new DoublePipe();
|
||||||
|
}
|
||||||
|
}
|
|
@ -103,7 +103,7 @@ function setUpChangeDetection(changeDetection:ChangeDetection, iterations) {
|
||||||
var parser = new Parser(new Lexer());
|
var parser = new Parser(new Lexer());
|
||||||
|
|
||||||
var parentProto = changeDetection.createProtoChangeDetector('parent');
|
var parentProto = changeDetection.createProtoChangeDetector('parent');
|
||||||
var parentCd = parentProto.instantiate(dispatcher, MapWrapper.create());
|
var parentCd = parentProto.instantiate(dispatcher);
|
||||||
|
|
||||||
var proto = changeDetection.createProtoChangeDetector("proto");
|
var proto = changeDetection.createProtoChangeDetector("proto");
|
||||||
var astWithSource = [
|
var astWithSource = [
|
||||||
|
@ -127,7 +127,7 @@ function setUpChangeDetection(changeDetection:ChangeDetection, iterations) {
|
||||||
for (var j = 0; j < 10; ++j) {
|
for (var j = 0; j < 10; ++j) {
|
||||||
obj.setField(j, i);
|
obj.setField(j, i);
|
||||||
}
|
}
|
||||||
var cd = proto.instantiate(dispatcher, null);
|
var cd = proto.instantiate(dispatcher);
|
||||||
cd.setContext(obj);
|
cd.setContext(obj);
|
||||||
parentCd.addChild(cd);
|
parentCd.addChild(cd);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue