fix(compiler): add missing support to string literals

Fixes #531
Closes #559
This commit is contained in:
Marc Laval 2015-02-05 20:13:32 +01:00
parent 6dbfe0dc2e
commit cf169f13a0
8 changed files with 73 additions and 8 deletions

View File

@ -79,6 +79,10 @@ export class Parser {
return new ASTWithSource(new Interpolation(strings, expressions), input, location); return new ASTWithSource(new Interpolation(strings, expressions), input, location);
} }
wrapLiteralPrimitive(input:string, location:any):ASTWithSource {
return new ASTWithSource(new LiteralPrimitive(input), input, location);
}
} }
class _ParseAST { class _ParseAST {

View File

@ -35,6 +35,6 @@ export function createDefaultSteps(
new ElementBindingMarker(), new ElementBindingMarker(),
new ProtoViewBuilder(changeDetection, shadowDomStrategy), new ProtoViewBuilder(changeDetection, shadowDomStrategy),
new ProtoElementInjectorBuilder(), new ProtoElementInjectorBuilder(),
new ElementBinderBuilder() new ElementBinderBuilder(parser, compilationUnit)
]; ];
} }

View File

@ -1,4 +1,4 @@
import {isPresent, BaseException} from 'angular2/src/facade/lang'; import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
import {List, MapWrapper} from 'angular2/src/facade/collection'; import {List, MapWrapper} from 'angular2/src/facade/collection';
import {TemplateElement} from 'angular2/src/facade/dom'; import {TemplateElement} from 'angular2/src/facade/dom';
import {SelectorMatcher} from '../selector'; import {SelectorMatcher} from '../selector';
@ -50,7 +50,10 @@ export class DirectiveParser extends CompileStep {
cssSelector.addClassName(classList[i]); cssSelector.addClassName(classList[i]);
} }
MapWrapper.forEach(attrs, (attrValue, attrName) => { MapWrapper.forEach(attrs, (attrValue, attrName) => {
if (isBlank(current.propertyBindings) ||
isPresent(current.propertyBindings) && !MapWrapper.contains(current.propertyBindings, attrName)) {
cssSelector.addAttribute(attrName, attrValue); cssSelector.addAttribute(attrName, attrValue);
}
}); });
if (isPresent(current.propertyBindings)) { if (isPresent(current.propertyBindings)) {
MapWrapper.forEach(current.propertyBindings, (expression, prop) => { MapWrapper.forEach(current.propertyBindings, (expression, prop) => {

View File

@ -86,6 +86,13 @@ function styleSetterFactory(styleName:string, stylesuffix:string) {
* with the flag `isViewRoot`. * with the flag `isViewRoot`.
*/ */
export class ElementBinderBuilder extends CompileStep { export class ElementBinderBuilder extends CompileStep {
_parser:Parser;
_compilationUnit:any;
constructor(parser:Parser, compilationUnit:any) {
this._parser = parser;
this._compilationUnit = compilationUnit;
}
process(parent:CompileElement, current:CompileElement, control:CompileControl) { process(parent:CompileElement, current:CompileElement, control:CompileControl) {
var elementBinder = null; var elementBinder = null;
if (current.hasBindings) { if (current.hasBindings) {
@ -149,14 +156,20 @@ export class ElementBinderBuilder extends CompileStep {
var directive = ListWrapper.get(directives, directiveIndex); var directive = ListWrapper.get(directives, directiveIndex);
var annotation = directive.annotation; var annotation = directive.annotation;
if (isBlank(annotation.bind)) continue; if (isBlank(annotation.bind)) continue;
var _this = this;
StringMapWrapper.forEach(annotation.bind, function (dirProp, elProp) { StringMapWrapper.forEach(annotation.bind, function (dirProp, elProp) {
var expression = isPresent(compileElement.propertyBindings) ? var expression = isPresent(compileElement.propertyBindings) ?
MapWrapper.get(compileElement.propertyBindings, elProp) : MapWrapper.get(compileElement.propertyBindings, elProp) :
null; null;
if (isBlank(expression)) { if (isBlank(expression)) {
var attributeValue = MapWrapper.get(compileElement.attrs(), elProp);
if (isPresent(attributeValue)) {
expression = _this._parser.wrapLiteralPrimitive(attributeValue, _this._compilationUnit);
} else {
throw new BaseException("No element binding found for property '" + elProp throw new BaseException("No element binding found for property '" + elProp
+ "' which is required by directive '" + stringify(directive.type) + "'"); + "' which is required by directive '" + stringify(directive.type) + "'");
} }
}
var len = dirProp.length; var len = dirProp.length;
var dirBindingName = dirProp; var dirBindingName = dirProp;
var isContentWatch = dirProp[len - 2] === '[' && dirProp[len - 1] === ']'; var isContentWatch = dirProp[len - 2] === '[' && dirProp[len - 1] === ']';

View File

@ -543,6 +543,12 @@ export function main() {
expect(ast.expressions[1].name).toEqual('b'); expect(ast.expressions[1].name).toEqual('b');
}); });
}); });
describe('wrapLiteralPrimitive', () => {
it('should wrap a literal primitive', () => {
expect(createParser().wrapLiteralPrimitive("foo", null).eval(null)).toEqual("foo");
});
});
}); });
} }

View File

@ -65,14 +65,23 @@ export function main() {
}); });
it('should consume directive watch expression change.', (done) => { it('should consume directive watch expression change.', (done) => {
compiler.compile(MyComp, el('<div my-dir [elprop]="ctxProp"></div>')).then((pv) => { var tpl =
'<div>' +
'<div my-dir [elprop]="ctxProp"></div>' +
'<div my-dir elprop="Hi there!"></div>' +
'<div my-dir elprop="Hi {{\'there!\'}}"></div>' +
'<div my-dir elprop="One more {{ctxProp}}"></div>' +
'</div>'
compiler.compile(MyComp, el(tpl)).then((pv) => {
createView(pv); createView(pv);
ctx.ctxProp = 'Hello World!'; ctx.ctxProp = 'Hello World!';
cd.detectChanges(); cd.detectChanges();
var elInj = view.elementInjectors[0]; expect(view.elementInjectors[0].get(MyDir).dirProp).toEqual('Hello World!');
expect(elInj.get(MyDir).dirProp).toEqual('Hello World!'); expect(view.elementInjectors[1].get(MyDir).dirProp).toEqual('Hi there!');
expect(view.elementInjectors[2].get(MyDir).dirProp).toEqual('Hi there!');
expect(view.elementInjectors[3].get(MyDir).dirProp).toEqual('One more Hello World!');
done(); done();
}); });
}); });

View File

@ -24,6 +24,7 @@ export function main() {
directives = [ directives = [
SomeDecorator, SomeDecorator,
SomeDecoratorIgnoringChildren, SomeDecoratorIgnoringChildren,
SomeDecoratorWithBinding,
SomeTemplate, SomeTemplate,
SomeTemplate2, SomeTemplate2,
SomeComponent, SomeComponent,
@ -182,6 +183,15 @@ export function main() {
); );
}).toThrowError('Only template directives are allowed on <template> elements!'); }).toThrowError('Only template directives are allowed on <template> elements!');
}); });
it('should not instantiate decorator directive twice', () => {
var pipeline = createPipeline({propertyBindings: {
'some-decor-with-binding': 'someExpr'
}});
var results = pipeline.process(el('<div some-decor-with-binding="foo"></div>'));
expect(results[0].decoratorDirectives.length).toEqual(1);
expect(results[0].decoratorDirectives).toEqual([reader.read(SomeDecoratorWithBinding)]);
});
}); });
}); });
} }
@ -208,6 +218,14 @@ class SomeDecorator {}
class SomeDecoratorIgnoringChildren { class SomeDecoratorIgnoringChildren {
} }
@Decorator({
selector: '[some-decor-with-binding]',
bind: {
'some-decor-with-binding': 'foo'
}
})
class SomeDecoratorWithBinding {}
@Template({ @Template({
selector: '[some-templ]' selector: '[some-templ]'
}) })

View File

@ -73,7 +73,7 @@ export function main() {
} else if (isPresent(parent)) { } else if (isPresent(parent)) {
current.inheritedProtoView = parent.inheritedProtoView; current.inheritedProtoView = parent.inheritedProtoView;
} }
}), new ElementBinderBuilder() }), new ElementBinderBuilder(parser, null)
]); ]);
} }
@ -312,6 +312,18 @@ export function main() {
expect(view.elementInjectors[1].get(SomeDecoratorDirectiveWithBinding).decorProp).toBe('a'); expect(view.elementInjectors[1].get(SomeDecoratorDirectiveWithBinding).decorProp).toBe('a');
}); });
it('should bind to string literals', () => {
var directives = [SomeDecoratorDirectiveWithBinding];
var protoElementInjector = new ProtoElementInjector(null, 0, directives);
var pipeline = createPipeline({directives: directives, protoElementInjector: protoElementInjector});
var results = pipeline.process(el('<div viewroot directives boundprop1="foo"></div>'));
var pv = results[0].inheritedProtoView;
instantiateView(pv);
changeDetector.detectChanges();
expect(view.elementInjectors[0].get(SomeDecoratorDirectiveWithBinding).decorProp).toEqual('foo');
});
describe('errors', () => { describe('errors', () => {
it('should throw if there is no element property bindings for a directive property binding', () => { it('should throw if there is no element property bindings for a directive property binding', () => {