fix(compiler): add missing support to string literals
Fixes #531 Closes #559
This commit is contained in:
parent
6dbfe0dc2e
commit
cf169f13a0
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) => {
|
||||||
cssSelector.addAttribute(attrName, attrValue);
|
if (isBlank(current.propertyBindings) ||
|
||||||
|
isPresent(current.propertyBindings) && !MapWrapper.contains(current.propertyBindings, attrName)) {
|
||||||
|
cssSelector.addAttribute(attrName, attrValue);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
if (isPresent(current.propertyBindings)) {
|
if (isPresent(current.propertyBindings)) {
|
||||||
MapWrapper.forEach(current.propertyBindings, (expression, prop) => {
|
MapWrapper.forEach(current.propertyBindings, (expression, prop) => {
|
||||||
|
|
|
@ -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,13 +156,19 @@ 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)) {
|
||||||
throw new BaseException("No element binding found for property '" + elProp
|
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
|
||||||
+ "' 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;
|
||||||
|
|
|
@ -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");
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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]'
|
||||||
})
|
})
|
||||||
|
|
|
@ -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', () => {
|
||||||
|
|
Loading…
Reference in New Issue