Skip to content

Commit cf169f1

Browse files
committed
fix(compiler): add missing support to string literals
Fixes angular#531 Closes angular#559
1 parent 6dbfe0d commit cf169f1

File tree

8 files changed

+73
-8
lines changed

8 files changed

+73
-8
lines changed

modules/angular2/src/change_detection/parser/parser.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ export class Parser {
7979
return new ASTWithSource(new Interpolation(strings, expressions), input, location);
8080
}
8181

82+
wrapLiteralPrimitive(input:string, location:any):ASTWithSource {
83+
return new ASTWithSource(new LiteralPrimitive(input), input, location);
84+
}
85+
8286
}
8387

8488
class _ParseAST {

modules/angular2/src/core/compiler/pipeline/default_steps.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,6 @@ export function createDefaultSteps(
3535
new ElementBindingMarker(),
3636
new ProtoViewBuilder(changeDetection, shadowDomStrategy),
3737
new ProtoElementInjectorBuilder(),
38-
new ElementBinderBuilder()
38+
new ElementBinderBuilder(parser, compilationUnit)
3939
];
4040
}

modules/angular2/src/core/compiler/pipeline/directive_parser.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {isPresent, BaseException} from 'angular2/src/facade/lang';
1+
import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
22
import {List, MapWrapper} from 'angular2/src/facade/collection';
33
import {TemplateElement} from 'angular2/src/facade/dom';
44
import {SelectorMatcher} from '../selector';
@@ -50,7 +50,10 @@ export class DirectiveParser extends CompileStep {
5050
cssSelector.addClassName(classList[i]);
5151
}
5252
MapWrapper.forEach(attrs, (attrValue, attrName) => {
53-
cssSelector.addAttribute(attrName, attrValue);
53+
if (isBlank(current.propertyBindings) ||
54+
isPresent(current.propertyBindings) && !MapWrapper.contains(current.propertyBindings, attrName)) {
55+
cssSelector.addAttribute(attrName, attrValue);
56+
}
5457
});
5558
if (isPresent(current.propertyBindings)) {
5659
MapWrapper.forEach(current.propertyBindings, (expression, prop) => {

modules/angular2/src/core/compiler/pipeline/element_binder_builder.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,13 @@ function styleSetterFactory(styleName:string, stylesuffix:string) {
8686
* with the flag `isViewRoot`.
8787
*/
8888
export class ElementBinderBuilder extends CompileStep {
89+
_parser:Parser;
90+
_compilationUnit:any;
91+
constructor(parser:Parser, compilationUnit:any) {
92+
this._parser = parser;
93+
this._compilationUnit = compilationUnit;
94+
}
95+
8996
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
9097
var elementBinder = null;
9198
if (current.hasBindings) {
@@ -149,13 +156,19 @@ export class ElementBinderBuilder extends CompileStep {
149156
var directive = ListWrapper.get(directives, directiveIndex);
150157
var annotation = directive.annotation;
151158
if (isBlank(annotation.bind)) continue;
159+
var _this = this;
152160
StringMapWrapper.forEach(annotation.bind, function (dirProp, elProp) {
153161
var expression = isPresent(compileElement.propertyBindings) ?
154162
MapWrapper.get(compileElement.propertyBindings, elProp) :
155163
null;
156164
if (isBlank(expression)) {
157-
throw new BaseException("No element binding found for property '" + elProp
165+
var attributeValue = MapWrapper.get(compileElement.attrs(), elProp);
166+
if (isPresent(attributeValue)) {
167+
expression = _this._parser.wrapLiteralPrimitive(attributeValue, _this._compilationUnit);
168+
} else {
169+
throw new BaseException("No element binding found for property '" + elProp
158170
+ "' which is required by directive '" + stringify(directive.type) + "'");
171+
}
159172
}
160173
var len = dirProp.length;
161174
var dirBindingName = dirProp;

modules/angular2/test/change_detection/parser/parser_spec.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,12 @@ export function main() {
543543
expect(ast.expressions[1].name).toEqual('b');
544544
});
545545
});
546+
547+
describe('wrapLiteralPrimitive', () => {
548+
it('should wrap a literal primitive', () => {
549+
expect(createParser().wrapLiteralPrimitive("foo", null).eval(null)).toEqual("foo");
550+
});
551+
});
546552
});
547553
}
548554

modules/angular2/test/core/compiler/integration_spec.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,23 @@ export function main() {
6565
});
6666

6767
it('should consume directive watch expression change.', (done) => {
68-
compiler.compile(MyComp, el('<div my-dir [elprop]="ctxProp"></div>')).then((pv) => {
68+
var tpl =
69+
'<div>' +
70+
'<div my-dir [elprop]="ctxProp"></div>' +
71+
'<div my-dir elprop="Hi there!"></div>' +
72+
'<div my-dir elprop="Hi {{\'there!\'}}"></div>' +
73+
'<div my-dir elprop="One more {{ctxProp}}"></div>' +
74+
'</div>'
75+
compiler.compile(MyComp, el(tpl)).then((pv) => {
6976
createView(pv);
7077

7178
ctx.ctxProp = 'Hello World!';
7279
cd.detectChanges();
7380

74-
var elInj = view.elementInjectors[0];
75-
expect(elInj.get(MyDir).dirProp).toEqual('Hello World!');
81+
expect(view.elementInjectors[0].get(MyDir).dirProp).toEqual('Hello World!');
82+
expect(view.elementInjectors[1].get(MyDir).dirProp).toEqual('Hi there!');
83+
expect(view.elementInjectors[2].get(MyDir).dirProp).toEqual('Hi there!');
84+
expect(view.elementInjectors[3].get(MyDir).dirProp).toEqual('One more Hello World!');
7685
done();
7786
});
7887
});

modules/angular2/test/core/compiler/pipeline/directive_parser_spec.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export function main() {
2424
directives = [
2525
SomeDecorator,
2626
SomeDecoratorIgnoringChildren,
27+
SomeDecoratorWithBinding,
2728
SomeTemplate,
2829
SomeTemplate2,
2930
SomeComponent,
@@ -182,6 +183,15 @@ export function main() {
182183
);
183184
}).toThrowError('Only template directives are allowed on <template> elements!');
184185
});
186+
187+
it('should not instantiate decorator directive twice', () => {
188+
var pipeline = createPipeline({propertyBindings: {
189+
'some-decor-with-binding': 'someExpr'
190+
}});
191+
var results = pipeline.process(el('<div some-decor-with-binding="foo"></div>'));
192+
expect(results[0].decoratorDirectives.length).toEqual(1);
193+
expect(results[0].decoratorDirectives).toEqual([reader.read(SomeDecoratorWithBinding)]);
194+
});
185195
});
186196
});
187197
}
@@ -208,6 +218,14 @@ class SomeDecorator {}
208218
class SomeDecoratorIgnoringChildren {
209219
}
210220

221+
@Decorator({
222+
selector: '[some-decor-with-binding]',
223+
bind: {
224+
'some-decor-with-binding': 'foo'
225+
}
226+
})
227+
class SomeDecoratorWithBinding {}
228+
211229
@Template({
212230
selector: '[some-templ]'
213231
})

modules/angular2/test/core/compiler/pipeline/element_binder_builder_spec.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export function main() {
7373
} else if (isPresent(parent)) {
7474
current.inheritedProtoView = parent.inheritedProtoView;
7575
}
76-
}), new ElementBinderBuilder()
76+
}), new ElementBinderBuilder(parser, null)
7777
]);
7878
}
7979

@@ -312,6 +312,18 @@ export function main() {
312312
expect(view.elementInjectors[1].get(SomeDecoratorDirectiveWithBinding).decorProp).toBe('a');
313313
});
314314

315+
it('should bind to string literals', () => {
316+
var directives = [SomeDecoratorDirectiveWithBinding];
317+
var protoElementInjector = new ProtoElementInjector(null, 0, directives);
318+
var pipeline = createPipeline({directives: directives, protoElementInjector: protoElementInjector});
319+
var results = pipeline.process(el('<div viewroot directives boundprop1="foo"></div>'));
320+
var pv = results[0].inheritedProtoView;
321+
instantiateView(pv);
322+
changeDetector.detectChanges();
323+
324+
expect(view.elementInjectors[0].get(SomeDecoratorDirectiveWithBinding).decorProp).toEqual('foo');
325+
});
326+
315327
describe('errors', () => {
316328

317329
it('should throw if there is no element property bindings for a directive property binding', () => {

0 commit comments

Comments
 (0)