From 685a6507b65a5a19bdd254af7afda14be40df422 Mon Sep 17 00:00:00 2001 From: vsavkin Date: Wed, 13 May 2015 15:28:03 -0700 Subject: [PATCH] feat(compiler): added support for [()] syntax --- modules/angular2/src/core/compiler/view.js | 2 +- .../dom/compiler/property_binding_parser.js | 50 ++++++++++++------- .../test/core/compiler/integration_spec.js | 42 ++++++++++++++++ .../compiler/property_binding_parser_spec.js | 12 +++++ 4 files changed, 86 insertions(+), 20 deletions(-) diff --git a/modules/angular2/src/core/compiler/view.js b/modules/angular2/src/core/compiler/view.js index a1a4c5183c..b016f86037 100644 --- a/modules/angular2/src/core/compiler/view.js +++ b/modules/angular2/src/core/compiler/view.js @@ -151,7 +151,7 @@ export class AppView { } var result = expr.eval(context, new Locals(this.locals, locals)); if (isPresent(result)) { - allowDefaultBehavior = allowDefaultBehavior && result; + allowDefaultBehavior = allowDefaultBehavior && result == true; } }); } diff --git a/modules/angular2/src/render/dom/compiler/property_binding_parser.js b/modules/angular2/src/render/dom/compiler/property_binding_parser.js index 978853230e..d868ac73bd 100644 --- a/modules/angular2/src/render/dom/compiler/property_binding_parser.js +++ b/modules/angular2/src/render/dom/compiler/property_binding_parser.js @@ -12,12 +12,13 @@ import {dashCaseToCamelCase} from '../util'; // Group 1 = "bind-" // Group 2 = "var-" or "#" // Group 3 = "on-" -// Group 4 = the identifier after "bind-", "var-/#", or "on-" -// Group 5 = idenitifer inside square braces -// Group 6 = identifier inside parenthesis +// Group 4 = "bindon-" +// Group 5 = the identifier after "bind-", "var-/#", or "on-" +// Group 6 = idenitifer inside [()] +// Group 7 = idenitifer inside [] +// Group 8 = identifier inside () var BIND_NAME_REGEXP = RegExpWrapper.create( - '^(?:(?:(?:(bind-)|(var-|#)|(on-))(.+))|\\[([^\\]]+)\\]|\\(([^\\)]+)\\))$'); - + '^(?:(?:(?:(bind-)|(var-|#)|(on-)|(bindon-))(.+))|\\[\\(([^\\)]+)\\)\\]|\\[([^\\]]+)\\]|\\(([^\\)]+)\\))$'); /** * Parses the property bindings on a single element. */ @@ -36,23 +37,30 @@ export class PropertyBindingParser extends CompileStep { MapWrapper.forEach(attrs, (attrValue, attrName) => { var bindParts = RegExpWrapper.firstMatch(BIND_NAME_REGEXP, attrName); if (isPresent(bindParts)) { - if (isPresent(bindParts[1])) { - // match: bind-prop - this._bindProperty(bindParts[4], attrValue, current, newAttrs); - } else if (isPresent(bindParts[2])) { - // match: var-name / var-name="iden" / #name / #name="iden" - var identifier = bindParts[4]; + if (isPresent(bindParts[1])) { // match: bind-prop + this._bindProperty(bindParts[5], attrValue, current, newAttrs); + + } else if (isPresent(bindParts[2])) { // match: var-name / var-name="iden" / #name / #name="iden" + var identifier = bindParts[5]; var value = attrValue == '' ? '\$implicit' : attrValue; this._bindVariable(identifier, value, current, newAttrs); - } else if (isPresent(bindParts[3])) { - // match: on-event - this._bindEvent(bindParts[4], attrValue, current, newAttrs); - } else if (isPresent(bindParts[5])) { - // match: [prop] + + } else if (isPresent(bindParts[3])) { // match: on-event + this._bindEvent(bindParts[5], attrValue, current, newAttrs); + + } else if (isPresent(bindParts[4])) { // match: bindon-prop this._bindProperty(bindParts[5], attrValue, current, newAttrs); - } else if (isPresent(bindParts[6])) { - // match: (event) - this._bindEvent(bindParts[6], attrValue, current, newAttrs); + this._bindAssignmentEvent(bindParts[5], attrValue, current, newAttrs); + + } else if (isPresent(bindParts[6])) { // match: [(expr)] + this._bindProperty(bindParts[6], attrValue, current, newAttrs); + this._bindAssignmentEvent(bindParts[6], attrValue, current, newAttrs); + + } else if (isPresent(bindParts[7])) { // match: [expr] + this._bindProperty(bindParts[7], attrValue, current, newAttrs); + + } else if (isPresent(bindParts[8])) { // match: (event) + this._bindEvent(bindParts[8], attrValue, current, newAttrs); } } else { var expr = this._parser.parseInterpolation( @@ -90,6 +98,10 @@ export class PropertyBindingParser extends CompileStep { MapWrapper.set(newAttrs, name, ast.source); } + _bindAssignmentEvent(name, expression, current:CompileElement, newAttrs) { + this._bindEvent(name, `${expression}=$event`, current, newAttrs); + } + _bindEvent(name, expression, current:CompileElement, newAttrs) { current.bindElement().bindEvent( dashCaseToCamelCase(name), this._parser.parseAction(expression, current.elementDescription) diff --git a/modules/angular2/test/core/compiler/integration_spec.js b/modules/angular2/test/core/compiler/integration_spec.js index 73ea7ac648..0ea48dac7e 100644 --- a/modules/angular2/test/core/compiler/integration_spec.js +++ b/modules/angular2/test/core/compiler/integration_spec.js @@ -603,6 +603,30 @@ export function main() { }); })); + it('should support [()] syntax', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideView(MyComp, new View({ + template: '
', + directives: [DirectiveWithTwoWayBinding] + })); + + tb.createView(MyComp, {context: ctx}).then((view) => { + var injector = view.rawView.elementInjectors[0]; + var dir = injector.get(DirectiveWithTwoWayBinding); + + ctx.ctxProp = 'one'; + view.detectChanges(); + + expect(dir.value).toEqual('one'); + + ObservableWrapper.subscribe(dir.control, (_) => { + expect(ctx.ctxProp).toEqual('two'); + async.done(); + }); + + dir.triggerChange('two'); + }); + })); + if (DOM.supportsDOMEvents()) { it("should support invoking methods on the host element via hostActions", inject([TestBed, AsyncTestCompleter], (tb, async) => { tb.overrideView(MyComp, new View({ @@ -1529,3 +1553,21 @@ class ToolbarComponent { this.query = query; } } + +@Directive({ + selector: '[two-way]', + properties: {value: 'control'}, + events: ['control'] +}) +class DirectiveWithTwoWayBinding { + control:EventEmitter; + value:any; + + constructor() { + this.control = new EventEmitter(); + } + + triggerChange(value) { + ObservableWrapper.callNext(this.control, value); + } +} diff --git a/modules/angular2/test/render/dom/compiler/property_binding_parser_spec.js b/modules/angular2/test/render/dom/compiler/property_binding_parser_spec.js index 3484091bbb..4c8caf885b 100644 --- a/modules/angular2/test/render/dom/compiler/property_binding_parser_spec.js +++ b/modules/angular2/test/render/dom/compiler/property_binding_parser_spec.js @@ -149,6 +149,18 @@ export function main() { expect(MapWrapper.get(results[0].attrs(), 'a')).toEqual('b'); expect(MapWrapper.get(results[0].attrs(), 'c')).toEqual('d'); }); + + it('should detect [()] syntax', () => { + var results = process(el('
')); + expect(MapWrapper.get(results[0].propertyBindings, 'a').source).toEqual('b'); + expect(results[0].eventBindings[0].source.source).toEqual('b=$event'); + }); + + it('should detect bindon- syntax', () => { + var results = process(el('
')); + expect(MapWrapper.get(results[0].propertyBindings, 'a').source).toEqual('b'); + expect(results[0].eventBindings[0].source.source).toEqual('b=$event'); + }); }); }