feat(change_detection): add support for pipes in the template
This commit is contained in:
@ -14,7 +14,6 @@ import {
|
||||
RECORD_TYPE_INVOKE_CLOSURE,
|
||||
RECORD_TYPE_PRIMITIVE_OP,
|
||||
RECORD_TYPE_KEYED_ACCESS,
|
||||
RECORD_TYPE_INVOKE_FORMATTER,
|
||||
RECORD_TYPE_PIPE,
|
||||
RECORD_TYPE_INTERPOLATE
|
||||
} from './proto_record';
|
||||
@ -26,10 +25,9 @@ import {
|
||||
*
|
||||
* 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);
|
||||
* this.dispatcher = dispatcher;
|
||||
* this.formatters = formatters;
|
||||
* this.protos = protos;
|
||||
*
|
||||
* this.context = null;
|
||||
@ -89,12 +87,11 @@ import {
|
||||
var ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
|
||||
var UTIL = "ChangeDetectionUtil";
|
||||
var DISPATCHER_ACCESSOR = "this.dispatcher";
|
||||
var FORMATTERS_ACCESSOR = "this.formatters";
|
||||
var PIPE_REGISTRY_ACCESSOR = "this.pipeRegistry";
|
||||
var PROTOS_ACCESSOR = "this.protos";
|
||||
var CHANGE_LOCAL = "change";
|
||||
var CHANGES_LOCAL = "changes";
|
||||
var TEMP_LOCAL = "temp";
|
||||
var PIPE_REGISTRY_ACCESSOR = "this.pipeRegistry";
|
||||
|
||||
function typeTemplate(type:string, cons:string, detectChanges:string, setContext:string):string {
|
||||
return `
|
||||
@ -102,18 +99,17 @@ ${cons}
|
||||
${detectChanges}
|
||||
${setContext};
|
||||
|
||||
return function(dispatcher, formatters, pipeRegistry) {
|
||||
return new ${type}(dispatcher, formatters, pipeRegistry, protos);
|
||||
return function(dispatcher, pipeRegistry) {
|
||||
return new ${type}(dispatcher, pipeRegistry, protos);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
function constructorTemplate(type:string, fieldsDefinitions:string):string {
|
||||
return `
|
||||
var ${type} = function ${type}(dispatcher, formatters, pipeRegistry, protos) {
|
||||
var ${type} = function ${type}(dispatcher, pipeRegistry, protos) {
|
||||
${ABSTRACT_CHANGE_DETECTOR}.call(this);
|
||||
${DISPATCHER_ACCESSOR} = dispatcher;
|
||||
${FORMATTERS_ACCESSOR} = formatters;
|
||||
${PIPE_REGISTRY_ACCESSOR} = pipeRegistry;
|
||||
${PROTOS_ACCESSOR} = protos;
|
||||
${fieldsDefinitions}
|
||||
@ -381,9 +377,6 @@ export class ChangeDetectorJITGenerator {
|
||||
case RECORD_TYPE_INTERPOLATE:
|
||||
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:
|
||||
var key = this.localNames[r.args[0]];
|
||||
return assignmentTemplate(newValue, `${context}[${key}]`);
|
||||
|
@ -16,7 +16,6 @@ import {
|
||||
RECORD_TYPE_INVOKE_CLOSURE,
|
||||
RECORD_TYPE_PRIMITIVE_OP,
|
||||
RECORD_TYPE_KEYED_ACCESS,
|
||||
RECORD_TYPE_INVOKE_FORMATTER,
|
||||
RECORD_TYPE_PIPE,
|
||||
RECORD_TYPE_INTERPOLATE
|
||||
} from './proto_record';
|
||||
@ -25,7 +24,6 @@ import {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} from './ex
|
||||
|
||||
export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
dispatcher:any;
|
||||
formatters:Map;
|
||||
pipeRegistry;
|
||||
|
||||
values:List;
|
||||
@ -35,10 +33,9 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
|
||||
protos:List<ProtoRecord>;
|
||||
|
||||
constructor(dispatcher:any, formatters:Map, pipeRegistry:PipeRegistry, protoRecords:List<ProtoRecord>) {
|
||||
constructor(dispatcher:any, pipeRegistry:PipeRegistry, protoRecords:List<ProtoRecord>) {
|
||||
super();
|
||||
this.dispatcher = dispatcher;
|
||||
this.formatters = formatters;
|
||||
this.pipeRegistry = pipeRegistry;
|
||||
|
||||
this.values = ListWrapper.createFixedSize(protoRecords.length + 1);
|
||||
@ -149,10 +146,6 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
case RECORD_TYPE_PRIMITIVE_OP:
|
||||
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:
|
||||
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;
|
||||
name:string;
|
||||
args:List<AST>;
|
||||
allArgs:List<AST>;
|
||||
constructor(exp:AST, name:string, args:List) {
|
||||
super();
|
||||
this.exp = exp;
|
||||
this.name = name;
|
||||
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) {
|
||||
@ -459,7 +443,6 @@ export class AstVisitor {
|
||||
visitBinary(ast:Binary) {}
|
||||
visitChain(ast:Chain){}
|
||||
visitConditional(ast:Conditional) {}
|
||||
visitFormatter(ast:Formatter) {}
|
||||
visitPipe(ast:Pipe) {}
|
||||
visitFunctionCall(ast:FunctionCall) {}
|
||||
visitImplicitReceiver(ast:ImplicitReceiver) {}
|
||||
|
@ -13,7 +13,6 @@ import {
|
||||
Binary,
|
||||
PrefixNot,
|
||||
Conditional,
|
||||
Formatter,
|
||||
Pipe,
|
||||
Assignment,
|
||||
Chain,
|
||||
@ -57,7 +56,7 @@ export class Parser {
|
||||
if (ListWrapper.isEmpty(pipes)) return bindingAst;
|
||||
|
||||
var res = ListWrapper.reduce(pipes,
|
||||
(result, currentPipeName) => new Pipe(result, currentPipeName),
|
||||
(result, currentPipeName) => new Pipe(result, currentPipeName, []),
|
||||
bindingAst.ast);
|
||||
return new ASTWithSource(res, bindingAst.source, bindingAst.location);
|
||||
}
|
||||
@ -191,7 +190,7 @@ class _ParseAST {
|
||||
parseChain():AST {
|
||||
var exprs = [];
|
||||
while (this.index < this.tokens.length) {
|
||||
var expr = this.parseFormatter();
|
||||
var expr = this.parsePipe();
|
||||
ListWrapper.push(exprs, expr);
|
||||
|
||||
if (this.optionalCharacter($SEMICOLON)) {
|
||||
@ -208,18 +207,18 @@ class _ParseAST {
|
||||
return new Chain(exprs);
|
||||
}
|
||||
|
||||
parseFormatter() {
|
||||
parsePipe() {
|
||||
var result = this.parseExpression();
|
||||
while (this.optionalOperator("|")) {
|
||||
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 args = ListWrapper.create();
|
||||
while (this.optionalCharacter($COLON)) {
|
||||
ListWrapper.push(args, this.parseExpression());
|
||||
}
|
||||
result = new Formatter(result, name, args);
|
||||
result = new Pipe(result, name, args);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -380,7 +379,7 @@ class _ParseAST {
|
||||
|
||||
parsePrimary() {
|
||||
if (this.optionalCharacter($LPAREN)) {
|
||||
var result = this.parseFormatter();
|
||||
var result = this.parsePipe();
|
||||
this.expectCharacter($RPAREN);
|
||||
return result;
|
||||
|
||||
|
@ -10,7 +10,6 @@ import {
|
||||
Binary,
|
||||
Chain,
|
||||
Conditional,
|
||||
Formatter,
|
||||
Pipe,
|
||||
FunctionCall,
|
||||
ImplicitReceiver,
|
||||
@ -40,14 +39,13 @@ import {
|
||||
RECORD_TYPE_INVOKE_CLOSURE,
|
||||
RECORD_TYPE_PRIMITIVE_OP,
|
||||
RECORD_TYPE_KEYED_ACCESS,
|
||||
RECORD_TYPE_INVOKE_FORMATTER,
|
||||
RECORD_TYPE_PIPE,
|
||||
RECORD_TYPE_INTERPOLATE
|
||||
} from './proto_record';
|
||||
|
||||
export class ProtoChangeDetector {
|
||||
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null){}
|
||||
instantiate(dispatcher:any, formatters:Map):ChangeDetector{
|
||||
instantiate(dispatcher:any):ChangeDetector{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -68,10 +66,9 @@ export class DynamicProtoChangeDetector extends ProtoChangeDetector {
|
||||
this._recordBuilder.addAst(ast, bindingMemento, directiveMemento);
|
||||
}
|
||||
|
||||
instantiate(dispatcher:any, formatters:Map) {
|
||||
instantiate(dispatcher:any) {
|
||||
this._createRecordsIfNecessary();
|
||||
return new DynamicChangeDetector(dispatcher, formatters,
|
||||
this._pipeRegistry, this._records);
|
||||
return new DynamicChangeDetector(dispatcher, this._pipeRegistry, this._records);
|
||||
}
|
||||
|
||||
_createRecordsIfNecessary() {
|
||||
@ -99,9 +96,9 @@ export class JitProtoChangeDetector extends ProtoChangeDetector {
|
||||
this._recordBuilder.addAst(ast, bindingMemento, directiveMemento);
|
||||
}
|
||||
|
||||
instantiate(dispatcher:any, formatters:Map) {
|
||||
instantiate(dispatcher:any) {
|
||||
this._createFactoryIfNecessary();
|
||||
return this._factory(dispatcher, formatters, this._pipeRegistry);
|
||||
return this._factory(dispatcher, this._pipeRegistry);
|
||||
}
|
||||
|
||||
_createFactoryIfNecessary() {
|
||||
@ -178,10 +175,6 @@ class _ConvertAstIntoProtoRecords {
|
||||
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) {
|
||||
var receiver = ast.receiver.visit(this);
|
||||
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_CLOSURE = 5;
|
||||
export const RECORD_TYPE_KEYED_ACCESS = 6;
|
||||
export const RECORD_TYPE_INVOKE_FORMATTER = 7;
|
||||
export const RECORD_TYPE_PIPE = 8;
|
||||
export const RECORD_TYPE_INTERPOLATE = 9;
|
||||
|
||||
@ -54,7 +53,6 @@ export class ProtoRecord {
|
||||
|
||||
isPureFunction():boolean {
|
||||
return this.mode === RECORD_TYPE_INTERPOLATE ||
|
||||
this.mode === RECORD_TYPE_INVOKE_FORMATTER ||
|
||||
this.mode === RECORD_TYPE_PRIMITIVE_OP;
|
||||
}
|
||||
}
|
||||
|
4
modules/angular2/src/core/compiler/view.js
vendored
4
modules/angular2/src/core/compiler/view.js
vendored
@ -19,8 +19,6 @@ import {EventManager} from 'angular2/src/core/events/event_manager';
|
||||
|
||||
const NG_BINDING_CLASS = '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.
|
||||
var VIEW_POOL_CAPACITY = 10000;
|
||||
@ -50,7 +48,7 @@ export class View {
|
||||
constructor(proto:ProtoView, nodes:List<Node>, protoChangeDetector:ProtoChangeDetector, protoContextLocals:Map) {
|
||||
this.proto = proto;
|
||||
this.nodes = nodes;
|
||||
this.changeDetector = protoChangeDetector.instantiate(this, NO_FORMATTERS);
|
||||
this.changeDetector = protoChangeDetector.instantiate(this);
|
||||
this.elementInjectors = null;
|
||||
this.rootElementInjectors = null;
|
||||
this.textNodes = null;
|
||||
|
Reference in New Issue
Block a user