feat(change_detection): change binding syntax to explicitly specify pipes
This commit is contained in:
+20
-13
@@ -3,6 +3,7 @@ import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, IS_DAR
|
||||
import {isPresent, isBlank, isJsObject, BaseException, FunctionWrapper} from 'angular2/src/facade/lang';
|
||||
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 {Lexer} from 'angular2/src/change_detection/parser/lexer';
|
||||
|
||||
@@ -29,9 +30,13 @@ export function main() {
|
||||
}
|
||||
|
||||
function createChangeDetector(memo:string, exp:string, context = null, formatters = null,
|
||||
registry = null, structural = false) {
|
||||
registry = null, pipeType:string = null) {
|
||||
var pcd = createProtoChangeDetector(registry);
|
||||
pcd.addAst(ast(exp), memo, memo, structural);
|
||||
var parsedAst = ast(exp);
|
||||
if (isPresent(pipeType)) {
|
||||
parsedAst = new Pipe(parsedAst, pipeType);
|
||||
}
|
||||
pcd.addAst(parsedAst, memo, memo);
|
||||
|
||||
var dispatcher = new TestDispatcher();
|
||||
var cd = pcd.instantiate(dispatcher, formatters);
|
||||
@@ -40,9 +45,8 @@ export function main() {
|
||||
return {"changeDetector" : cd, "dispatcher" : dispatcher};
|
||||
}
|
||||
|
||||
function executeWatch(memo:string, exp:string, context = null, formatters = null,
|
||||
registry = null, content = false) {
|
||||
var res = createChangeDetector(memo, exp, context, formatters, registry, content);
|
||||
function executeWatch(memo:string, exp:string, context = null, formatters = null) {
|
||||
var res = createChangeDetector(memo, exp, context, formatters);
|
||||
res["changeDetector"].detectChanges();
|
||||
return res["dispatcher"].log;
|
||||
}
|
||||
@@ -190,7 +194,7 @@ export function main() {
|
||||
var parser = new Parser(new Lexer());
|
||||
var pcd = createProtoChangeDetector();
|
||||
var ast = parser.parseInterpolation("B{{a}}A", "location");
|
||||
pcd.addAst(ast, "memo", "memo", false);
|
||||
pcd.addAst(ast, "memo", "memo");
|
||||
|
||||
var dispatcher = new TestDispatcher();
|
||||
var cd = pcd.instantiate(dispatcher, MapWrapper.create());
|
||||
@@ -428,10 +432,10 @@ export function main() {
|
||||
|
||||
describe("pipes", () => {
|
||||
it("should support pipes", () => {
|
||||
var registry = new FakePipeRegistry(() => new CountingPipe());
|
||||
var registry = new FakePipeRegistry('pipe', () => new CountingPipe());
|
||||
var ctx = new Person("Megatron");
|
||||
|
||||
var c = createChangeDetector("memo", "name", ctx, null, registry, true);
|
||||
var c = createChangeDetector("memo", "name", ctx, null, registry, 'pipe');
|
||||
var cd = c["changeDetector"];
|
||||
var dispatcher = c["dispatcher"];
|
||||
|
||||
@@ -446,10 +450,10 @@ export function main() {
|
||||
});
|
||||
|
||||
it("should lookup pipes in the registry when the context is not supported", () => {
|
||||
var registry = new FakePipeRegistry(() => new OncePipe());
|
||||
var registry = new FakePipeRegistry('pipe', () => new OncePipe());
|
||||
var ctx = new Person("Megatron");
|
||||
|
||||
var c = createChangeDetector("memo", "name", ctx, null, registry, true);
|
||||
var c = createChangeDetector("memo", "name", ctx, null, registry, 'pipe');
|
||||
var cd = c["changeDetector"];
|
||||
|
||||
cd.detectChanges();
|
||||
@@ -464,10 +468,10 @@ export function main() {
|
||||
});
|
||||
|
||||
it("should do nothing when returns NO_CHANGE", () => {
|
||||
var registry = new FakePipeRegistry(() => new IdentityPipe())
|
||||
var registry = new FakePipeRegistry('pipe', () => new IdentityPipe())
|
||||
var ctx = new Person("Megatron");
|
||||
|
||||
var c = createChangeDetector("memo", "name", ctx, null, registry, true);
|
||||
var c = createChangeDetector("memo", "name", ctx, null, registry, 'pipe');
|
||||
var cd = c["changeDetector"];
|
||||
var dispatcher = c["dispatcher"];
|
||||
|
||||
@@ -537,15 +541,18 @@ class IdentityPipe {
|
||||
|
||||
class FakePipeRegistry extends PipeRegistry {
|
||||
numberOfLookups:number;
|
||||
pipeType:string;
|
||||
factory:Function;
|
||||
|
||||
constructor(factory) {
|
||||
constructor(pipeType, factory) {
|
||||
super({});
|
||||
this.pipeType = pipeType;
|
||||
this.factory = factory;
|
||||
this.numberOfLookups = 0;
|
||||
}
|
||||
|
||||
get(type:string, obj) {
|
||||
if (type != this.pipeType) return null;
|
||||
this.numberOfLookups ++;
|
||||
return this.factory();
|
||||
}
|
||||
|
||||
@@ -51,6 +51,10 @@ export function main() {
|
||||
return createParser().parseInterpolation(text, location);
|
||||
}
|
||||
|
||||
function addPipes(ast, pipes) {
|
||||
return createParser().addPipes(ast, pipes);
|
||||
}
|
||||
|
||||
function expectEval(text, passedInContext = null) {
|
||||
var c = isBlank(passedInContext) ? td() : passedInContext;
|
||||
return expect(parseAction(text).eval(c));
|
||||
@@ -544,6 +548,29 @@ 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', () => {
|
||||
it('should wrap a literal primitive', () => {
|
||||
expect(createParser().wrapLiteralPrimitive("foo", null).eval(null)).toEqual("foo");
|
||||
|
||||
+20
-2
@@ -11,8 +11,8 @@ export function main() {
|
||||
it("should return the first pipe supporting the data type", () => {
|
||||
var r = new PipeRegistry({
|
||||
"type": [
|
||||
{"supports": (obj) => false, "pipe": () => firstPipe},
|
||||
{"supports": (obj) => true, "pipe": () => secondPipe}
|
||||
new PipeFactory(false, firstPipe),
|
||||
new PipeFactory(true, secondPipe)
|
||||
]
|
||||
});
|
||||
|
||||
@@ -37,3 +37,21 @@ export function main() {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class PipeFactory {
|
||||
shouldSupport:boolean;
|
||||
pipe:any;
|
||||
|
||||
constructor(shouldSupport:boolean, pipe:any) {
|
||||
this.shouldSupport = shouldSupport;
|
||||
this.pipe = pipe;
|
||||
}
|
||||
|
||||
supports(obj):boolean {
|
||||
return this.shouldSupport;
|
||||
}
|
||||
|
||||
create():Pipe {
|
||||
return this.pipe;
|
||||
}
|
||||
}
|
||||
+66
-6
@@ -1,5 +1,5 @@
|
||||
import {describe, beforeEach, it, expect, iit, ddescribe, el} from 'angular2/test_lib';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {isPresent, normalizeBlank} from 'angular2/src/facade/lang';
|
||||
import {DOM} from 'angular2/src/facade/dom';
|
||||
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
@@ -15,7 +15,7 @@ import {ProtoView, ElementPropertyMemento, DirectivePropertyMemento} from 'angul
|
||||
import {ProtoElementInjector} from 'angular2/src/core/compiler/element_injector';
|
||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||
|
||||
import {ChangeDetector, Lexer, Parser, DynamicProtoChangeDetector,
|
||||
import {ChangeDetector, Lexer, Parser, DynamicProtoChangeDetector, PipeRegistry, Pipe
|
||||
} from 'angular2/change_detection';
|
||||
import {Injector} from 'angular2/di';
|
||||
|
||||
@@ -23,8 +23,7 @@ export function main() {
|
||||
describe('ElementBinderBuilder', () => {
|
||||
var evalContext, view, changeDetector;
|
||||
|
||||
function createPipeline({textNodeBindings, propertyBindings, eventBindings, directives, protoElementInjector
|
||||
}={}) {
|
||||
function createPipeline({textNodeBindings, propertyBindings, eventBindings, directives, protoElementInjector, registry}={}) {
|
||||
var reflector = new DirectiveMetadataReader();
|
||||
var parser = new Parser(new Lexer());
|
||||
return new CompilePipeline([
|
||||
@@ -70,8 +69,10 @@ export function main() {
|
||||
}
|
||||
if (isPresent(current.element.getAttribute('viewroot'))) {
|
||||
current.isViewRoot = true;
|
||||
current.inheritedProtoView = new ProtoView(current.element,
|
||||
new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy());
|
||||
current.inheritedProtoView = new ProtoView(
|
||||
current.element,
|
||||
new DynamicProtoChangeDetector(normalizeBlank(registry)),
|
||||
new NativeShadowDomStrategy());
|
||||
} else if (isPresent(parent)) {
|
||||
current.inheritedProtoView = parent.inheritedProtoView;
|
||||
}
|
||||
@@ -393,6 +394,37 @@ export function main() {
|
||||
expect(view.elementInjectors[0].get(SomeComponentDirectiveWithBinding).compProp).toBe('c');
|
||||
});
|
||||
|
||||
it('should bind directive properties with pipes', () => {
|
||||
var propertyBindings = MapWrapper.createFromStringMap({
|
||||
'boundprop': 'prop1'
|
||||
});
|
||||
|
||||
var directives = [DirectiveWithBindingsThatHavePipes];
|
||||
var protoElementInjector = new ProtoElementInjector(null, 0, directives, true);
|
||||
|
||||
var registry = new PipeRegistry({
|
||||
"double" : [new DoublePipeFactory()]
|
||||
});
|
||||
|
||||
var pipeline = createPipeline({
|
||||
propertyBindings: propertyBindings,
|
||||
directives: directives,
|
||||
protoElementInjector: protoElementInjector,
|
||||
registry: registry
|
||||
});
|
||||
|
||||
var results = pipeline.process(el('<div viewroot prop-binding directives></div>'));
|
||||
var pv = results[0].inheritedProtoView;
|
||||
results[0].inheritedElementBinder.nestedProtoView = new ProtoView(
|
||||
el('<div></div>'), new DynamicProtoChangeDetector(registry), new NativeShadowDomStrategy());
|
||||
|
||||
instantiateView(pv);
|
||||
evalContext.prop1 = 'a';
|
||||
changeDetector.detectChanges();
|
||||
|
||||
expect(view.elementInjectors[0].get(DirectiveWithBindingsThatHavePipes).compProp).toEqual('aa');
|
||||
});
|
||||
|
||||
it('should bind directive properties for sibling elements', () => {
|
||||
var propertyBindings = MapWrapper.createFromStringMap({
|
||||
'boundprop1': 'prop1'
|
||||
@@ -504,6 +536,34 @@ class SomeComponentDirectiveWithBinding {
|
||||
}
|
||||
}
|
||||
|
||||
@Component({bind: {'compProp':'boundprop | double'}})
|
||||
class DirectiveWithBindingsThatHavePipes {
|
||||
compProp;
|
||||
constructor() {
|
||||
this.compProp = null;
|
||||
}
|
||||
}
|
||||
|
||||
class DoublePipe extends Pipe {
|
||||
supports(obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
transform(value) {
|
||||
return `${value}${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
class DoublePipeFactory {
|
||||
supports(obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
create() {
|
||||
return new DoublePipe();
|
||||
}
|
||||
}
|
||||
|
||||
class Context {
|
||||
prop1;
|
||||
prop2;
|
||||
|
||||
+5
-5
@@ -548,7 +548,7 @@ export function main() {
|
||||
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
|
||||
new DynamicProtoChangeDetector(null), null);
|
||||
pv.bindElement(new ProtoElementInjector(null, 0, [SomeDirective]));
|
||||
pv.bindDirectiveProperty(0, parser.parseBinding('foo', null), 'prop', reflector.setter('prop'), false);
|
||||
pv.bindDirectiveProperty(0, parser.parseBinding('foo', null), 'prop', reflector.setter('prop'));
|
||||
createViewAndChangeDetector(pv);
|
||||
|
||||
ctx.foo = 'buz';
|
||||
@@ -563,8 +563,8 @@ export function main() {
|
||||
pv.bindElement(new ProtoElementInjector(null, 0, [
|
||||
DirectiveBinding.createFromType(DirectiveImplementingOnChange, new Directive({lifecycle: [onChange]}))
|
||||
]));
|
||||
pv.bindDirectiveProperty( 0, parser.parseBinding('a', null), 'a', reflector.setter('a'), false);
|
||||
pv.bindDirectiveProperty( 0, parser.parseBinding('b', null), 'b', reflector.setter('b'), false);
|
||||
pv.bindDirectiveProperty( 0, parser.parseBinding('a', null), 'a', reflector.setter('a'));
|
||||
pv.bindDirectiveProperty( 0, parser.parseBinding('b', null), 'b', reflector.setter('b'));
|
||||
createViewAndChangeDetector(pv);
|
||||
|
||||
ctx.a = 100;
|
||||
@@ -582,8 +582,8 @@ export function main() {
|
||||
pv.bindElement(new ProtoElementInjector(null, 0, [
|
||||
DirectiveBinding.createFromType(DirectiveImplementingOnChange, new Directive({lifecycle: [onChange]}))
|
||||
]));
|
||||
pv.bindDirectiveProperty( 0, parser.parseBinding('a', null), 'a', reflector.setter('a'), false);
|
||||
pv.bindDirectiveProperty( 0, parser.parseBinding('b', null), 'b', reflector.setter('b'), false);
|
||||
pv.bindDirectiveProperty( 0, parser.parseBinding('a', null), 'a', reflector.setter('a'));
|
||||
pv.bindDirectiveProperty( 0, parser.parseBinding('b', null), 'b', reflector.setter('b'));
|
||||
createViewAndChangeDetector(pv);
|
||||
|
||||
ctx.a = 0;
|
||||
|
||||
Reference in New Issue
Block a user