Skip to content

Commit 685a650

Browse files
committed
feat(compiler): added support for [()] syntax
1 parent 8463544 commit 685a650

4 files changed

Lines changed: 86 additions & 20 deletions

File tree

modules/angular2/src/core/compiler/view.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ export class AppView {
151151
}
152152
var result = expr.eval(context, new Locals(this.locals, locals));
153153
if (isPresent(result)) {
154-
allowDefaultBehavior = allowDefaultBehavior && result;
154+
allowDefaultBehavior = allowDefaultBehavior && result == true;
155155
}
156156
});
157157
}

modules/angular2/src/render/dom/compiler/property_binding_parser.js

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ import {dashCaseToCamelCase} from '../util';
1212
// Group 1 = "bind-"
1313
// Group 2 = "var-" or "#"
1414
// Group 3 = "on-"
15-
// Group 4 = the identifier after "bind-", "var-/#", or "on-"
16-
// Group 5 = idenitifer inside square braces
17-
// Group 6 = identifier inside parenthesis
15+
// Group 4 = "bindon-"
16+
// Group 5 = the identifier after "bind-", "var-/#", or "on-"
17+
// Group 6 = idenitifer inside [()]
18+
// Group 7 = idenitifer inside []
19+
// Group 8 = identifier inside ()
1820
var BIND_NAME_REGEXP = RegExpWrapper.create(
19-
'^(?:(?:(?:(bind-)|(var-|#)|(on-))(.+))|\\[([^\\]]+)\\]|\\(([^\\)]+)\\))$');
20-
21+
'^(?:(?:(?:(bind-)|(var-|#)|(on-)|(bindon-))(.+))|\\[\\(([^\\)]+)\\)\\]|\\[([^\\]]+)\\]|\\(([^\\)]+)\\))$');
2122
/**
2223
* Parses the property bindings on a single element.
2324
*/
@@ -36,23 +37,30 @@ export class PropertyBindingParser extends CompileStep {
3637
MapWrapper.forEach(attrs, (attrValue, attrName) => {
3738
var bindParts = RegExpWrapper.firstMatch(BIND_NAME_REGEXP, attrName);
3839
if (isPresent(bindParts)) {
39-
if (isPresent(bindParts[1])) {
40-
// match: bind-prop
41-
this._bindProperty(bindParts[4], attrValue, current, newAttrs);
42-
} else if (isPresent(bindParts[2])) {
43-
// match: var-name / var-name="iden" / #name / #name="iden"
44-
var identifier = bindParts[4];
40+
if (isPresent(bindParts[1])) { // match: bind-prop
41+
this._bindProperty(bindParts[5], attrValue, current, newAttrs);
42+
43+
} else if (isPresent(bindParts[2])) { // match: var-name / var-name="iden" / #name / #name="iden"
44+
var identifier = bindParts[5];
4545
var value = attrValue == '' ? '\$implicit' : attrValue;
4646
this._bindVariable(identifier, value, current, newAttrs);
47-
} else if (isPresent(bindParts[3])) {
48-
// match: on-event
49-
this._bindEvent(bindParts[4], attrValue, current, newAttrs);
50-
} else if (isPresent(bindParts[5])) {
51-
// match: [prop]
47+
48+
} else if (isPresent(bindParts[3])) { // match: on-event
49+
this._bindEvent(bindParts[5], attrValue, current, newAttrs);
50+
51+
} else if (isPresent(bindParts[4])) { // match: bindon-prop
5252
this._bindProperty(bindParts[5], attrValue, current, newAttrs);
53-
} else if (isPresent(bindParts[6])) {
54-
// match: (event)
55-
this._bindEvent(bindParts[6], attrValue, current, newAttrs);
53+
this._bindAssignmentEvent(bindParts[5], attrValue, current, newAttrs);
54+
55+
} else if (isPresent(bindParts[6])) { // match: [(expr)]
56+
this._bindProperty(bindParts[6], attrValue, current, newAttrs);
57+
this._bindAssignmentEvent(bindParts[6], attrValue, current, newAttrs);
58+
59+
} else if (isPresent(bindParts[7])) { // match: [expr]
60+
this._bindProperty(bindParts[7], attrValue, current, newAttrs);
61+
62+
} else if (isPresent(bindParts[8])) { // match: (event)
63+
this._bindEvent(bindParts[8], attrValue, current, newAttrs);
5664
}
5765
} else {
5866
var expr = this._parser.parseInterpolation(
@@ -90,6 +98,10 @@ export class PropertyBindingParser extends CompileStep {
9098
MapWrapper.set(newAttrs, name, ast.source);
9199
}
92100

101+
_bindAssignmentEvent(name, expression, current:CompileElement, newAttrs) {
102+
this._bindEvent(name, `${expression}=$event`, current, newAttrs);
103+
}
104+
93105
_bindEvent(name, expression, current:CompileElement, newAttrs) {
94106
current.bindElement().bindEvent(
95107
dashCaseToCamelCase(name), this._parser.parseAction(expression, current.elementDescription)

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,30 @@ export function main() {
603603
});
604604
}));
605605

606+
it('should support [()] syntax', inject([TestBed, AsyncTestCompleter], (tb, async) => {
607+
tb.overrideView(MyComp, new View({
608+
template: '<div [(control)]="ctxProp" two-way></div>',
609+
directives: [DirectiveWithTwoWayBinding]
610+
}));
611+
612+
tb.createView(MyComp, {context: ctx}).then((view) => {
613+
var injector = view.rawView.elementInjectors[0];
614+
var dir = injector.get(DirectiveWithTwoWayBinding);
615+
616+
ctx.ctxProp = 'one';
617+
view.detectChanges();
618+
619+
expect(dir.value).toEqual('one');
620+
621+
ObservableWrapper.subscribe(dir.control, (_) => {
622+
expect(ctx.ctxProp).toEqual('two');
623+
async.done();
624+
});
625+
626+
dir.triggerChange('two');
627+
});
628+
}));
629+
606630
if (DOM.supportsDOMEvents()) {
607631
it("should support invoking methods on the host element via hostActions", inject([TestBed, AsyncTestCompleter], (tb, async) => {
608632
tb.overrideView(MyComp, new View({
@@ -1529,3 +1553,21 @@ class ToolbarComponent {
15291553
this.query = query;
15301554
}
15311555
}
1556+
1557+
@Directive({
1558+
selector: '[two-way]',
1559+
properties: {value: 'control'},
1560+
events: ['control']
1561+
})
1562+
class DirectiveWithTwoWayBinding {
1563+
control:EventEmitter;
1564+
value:any;
1565+
1566+
constructor() {
1567+
this.control = new EventEmitter();
1568+
}
1569+
1570+
triggerChange(value) {
1571+
ObservableWrapper.callNext(this.control, value);
1572+
}
1573+
}

modules/angular2/test/render/dom/compiler/property_binding_parser_spec.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,18 @@ export function main() {
149149
expect(MapWrapper.get(results[0].attrs(), 'a')).toEqual('b');
150150
expect(MapWrapper.get(results[0].attrs(), 'c')).toEqual('d');
151151
});
152+
153+
it('should detect [()] syntax', () => {
154+
var results = process(el('<div [(a)]="b"></div>'));
155+
expect(MapWrapper.get(results[0].propertyBindings, 'a').source).toEqual('b');
156+
expect(results[0].eventBindings[0].source.source).toEqual('b=$event');
157+
});
158+
159+
it('should detect bindon- syntax', () => {
160+
var results = process(el('<div bindon-a="b"></div>'));
161+
expect(MapWrapper.get(results[0].propertyBindings, 'a').source).toEqual('b');
162+
expect(results[0].eventBindings[0].source.source).toEqual('b=$event');
163+
});
152164
});
153165
}
154166

0 commit comments

Comments
 (0)