diff --git a/modules/core/src/compiler/view.js b/modules/core/src/compiler/view.js
index 564ae93526..fe11cf9723 100644
--- a/modules/core/src/compiler/view.js
+++ b/modules/core/src/compiler/view.js
@@ -373,6 +373,22 @@ export class ProtoView {
if (isPresent(elementInjector)) {
preBuiltObjects[i] = new PreBuiltObjects(view, new NgElement(element), viewPort, lightDom);
}
+
+ // events
+ if (isPresent(binder.events)) {
+ // TODO(rado): if there is directive at this element that injected an
+ // event emitter for that eventType do not attach the handler.
+ MapWrapper.forEach(binder.events, (expr, eventName) => {
+ DOM.on(element, eventName, (event) => {
+ if (event.target === element) {
+ // TODO(rado): replace with
+ // expr.eval(new ContextWithVariableBindings(view.context, {'$event': event}));
+ // when eval with variable bindinds works.
+ expr.eval(view.context);
+ }
+ });
+ });
+ }
}
view.init(elementInjectors, rootElementInjectors, textNodes, elementsWithPropertyBindings,
diff --git a/modules/core/test/compiler/view_spec.js b/modules/core/test/compiler/view_spec.js
index d27e3727aa..bea7b82d54 100644
--- a/modules/core/test/compiler/view_spec.js
+++ b/modules/core/test/compiler/view_spec.js
@@ -422,6 +422,37 @@ export function main() {
});
});
+ describe('event handlers', () => {
+ var view, ctx, called;
+
+ function createViewAndContext(protoView) {
+ view = createView(protoView);
+ ctx = view.context;
+ called = 0;
+ ctx.callMe = () => called += 1;
+ }
+
+ function dispatchClick(el) {
+ DOM.dispatchEvent(el, DOM.createMouseEvent('click'));
+ }
+
+ it('should fire on non-bubbling native events', () => {
+ var pv = new ProtoView(createElement('
'),
+ new ProtoRecordRange());
+ pv.bindElement(null);
+ pv.bindEvent('click', parser.parseBinding('callMe()', null));
+ createViewAndContext(pv);
+
+ dispatchClick(view.nodes[0]);
+ dispatchClick(view.nodes[0].firstChild);
+
+ // the bubbled event does not execute the expression.
+ // It is trivially passing on webkit browsers due to
+ // https://bugs.webkit.org/show_bug.cgi?id=122755
+ expect(called).toEqual(1);
+ });
+ });
+
describe('react to record changes', () => {
var view, cd, ctx;
@@ -602,6 +633,7 @@ class MyEvaluationContext {
foo:string;
a;
b;
+ callMe;
constructor() {
this.foo = 'bar';
};
diff --git a/modules/examples/e2e_test/hello_world/hello_world_spec.es6 b/modules/examples/e2e_test/hello_world/hello_world_spec.es6
index 8a58a38075..5612e55a5c 100644
--- a/modules/examples/e2e_test/hello_world/hello_world_spec.es6
+++ b/modules/examples/e2e_test/hello_world/hello_world_spec.es6
@@ -9,7 +9,7 @@ describe('hello world', function () {
it('should greet', function() {
browser.get(URL);
- expect(getShadowText('hello-app')).toBe('hello world!');
+ expect(getGreetingText('hello-app')).toBe('hello world!');
});
});
@@ -19,12 +19,12 @@ describe('hello world', function () {
it('should greet', function() {
browser.get(URL);
- expect(getShadowText('hello-app')).toBe('hello world!');
+ expect(getGreetingText('hello-app')).toBe('hello world!');
});
});
});
-function getShadowText(selector) {
- return browser.executeScript('return document.querySelector("'+selector+'").shadowRoot.textContent');
-}
\ No newline at end of file
+function getGreetingText(selector) {
+ return browser.executeScript('return document.querySelector("'+selector+'").shadowRoot.firstChild.textContent');
+}
diff --git a/modules/examples/src/hello_world/index_common.js b/modules/examples/src/hello_world/index_common.js
index 2e3ee8402a..b2950e4d51 100644
--- a/modules/examples/src/hello_world/index_common.js
+++ b/modules/examples/src/hello_world/index_common.js
@@ -20,7 +20,8 @@ import {bootstrap, Component, Decorator, TemplateConfig, NgElement} from 'core/c
// The template for the component.
// Expressions in the template (like {{greeting}}) are evaluated in the
// context of the HelloCmp class below.
- inline: `{{greeting}} world!`,
+ inline: `
{{greeting}} world!
+ `,
// All directives used in the template need to be specified. This allows for
// modularity (RedDec can only be used in this template)
// and better tooling (the template can be invalidated if the attribute is
@@ -33,6 +34,9 @@ class HelloCmp {
constructor(service: GreetingService) {
this.greeting = service.greeting;
}
+ changeGreeting() {
+ this.greeting = 'howdy';
+ }
}
// Decorators are light-weight. They don't allow for templates, or new
diff --git a/modules/facade/src/dom.dart b/modules/facade/src/dom.dart
index 1b247a2016..afcf329a58 100644
--- a/modules/facade/src/dom.dart
+++ b/modules/facade/src/dom.dart
@@ -33,7 +33,15 @@ class DOM {
return el.querySelectorAll(selector);
}
static on(element, event, callback) {
- element.addEventListener(event, callback);
+ // due to https://code.google.com/p/dart/issues/detail?id=17406
+ // addEventListener misses zones so we use element.on.
+ element.on[event].listen(callback);
+ }
+ static dispatchEvent(el, evt) {
+ el.dispatchEvent(evt);
+ }
+ static createMouseEvent(eventType) {
+ return new MouseEvent(eventType, canBubble: true);
}
static getInnerHTML(el) {
return el.innerHtml;
diff --git a/modules/facade/src/dom.es6 b/modules/facade/src/dom.es6
index aa06c390ef..8f1148acdf 100644
--- a/modules/facade/src/dom.es6
+++ b/modules/facade/src/dom.es6
@@ -24,6 +24,14 @@ export class DOM {
static on(el, evt, listener) {
el.addEventListener(evt, listener, false);
}
+ static dispatchEvent(el, evt) {
+ el.dispatchEvent(evt);
+ }
+ static createMouseEvent(eventType) {
+ var evt = new MouseEvent(eventType);
+ evt.initEvent(eventType, true, true);
+ return evt;
+ }
static getInnerHTML(el) {
return el.innerHTML;
}