Skip to content

Commit c5b0baf

Browse files
committed
feat(view): adds event binding to view instantiation.
Only native non-bubbling events are supported by the commit.
1 parent bccc863 commit c5b0baf

File tree

6 files changed

+75
-7
lines changed

6 files changed

+75
-7
lines changed

modules/core/src/compiler/view.js

+16
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,22 @@ export class ProtoView {
373373
if (isPresent(elementInjector)) {
374374
preBuiltObjects[i] = new PreBuiltObjects(view, new NgElement(element), viewPort, lightDom);
375375
}
376+
377+
// events
378+
if (isPresent(binder.events)) {
379+
// TODO(rado): if there is directive at this element that injected an
380+
// event emitter for that eventType do not attach the handler.
381+
MapWrapper.forEach(binder.events, (expr, eventName) => {
382+
DOM.on(element, eventName, (event) => {
383+
if (event.target === element) {
384+
// TODO(rado): replace with
385+
// expr.eval(new ContextWithVariableBindings(view.context, {'$event': event}));
386+
// when eval with variable bindinds works.
387+
expr.eval(view.context);
388+
}
389+
});
390+
});
391+
}
376392
}
377393

378394
view.init(elementInjectors, rootElementInjectors, textNodes, elementsWithPropertyBindings,

modules/core/test/compiler/view_spec.js

+32
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,37 @@ export function main() {
422422
});
423423
});
424424

425+
describe('event handlers', () => {
426+
var view, ctx, called;
427+
428+
function createViewAndContext(protoView) {
429+
view = createView(protoView);
430+
ctx = view.context;
431+
called = 0;
432+
ctx.callMe = () => called += 1;
433+
}
434+
435+
function dispatchClick(el) {
436+
DOM.dispatchEvent(el, DOM.createMouseEvent('click'));
437+
}
438+
439+
it('should fire on non-bubbling native events', () => {
440+
var pv = new ProtoView(createElement('<div class="ng-binding"><div></div></div>'),
441+
new ProtoRecordRange());
442+
pv.bindElement(null);
443+
pv.bindEvent('click', parser.parseBinding('callMe()', null));
444+
createViewAndContext(pv);
445+
446+
dispatchClick(view.nodes[0]);
447+
dispatchClick(view.nodes[0].firstChild);
448+
449+
// the bubbled event does not execute the expression.
450+
// It is trivially passing on webkit browsers due to
451+
// https://bugs.webkit.org/show_bug.cgi?id=122755
452+
expect(called).toEqual(1);
453+
});
454+
});
455+
425456
describe('react to record changes', () => {
426457
var view, cd, ctx;
427458

@@ -602,6 +633,7 @@ class MyEvaluationContext {
602633
foo:string;
603634
a;
604635
b;
636+
callMe;
605637
constructor() {
606638
this.foo = 'bar';
607639
};

modules/examples/e2e_test/hello_world/hello_world_spec.es6

+5-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ describe('hello world', function () {
99
it('should greet', function() {
1010
browser.get(URL);
1111

12-
expect(getShadowText('hello-app')).toBe('hello world!');
12+
expect(getGreetingText('hello-app')).toBe('hello world!');
1313
});
1414
});
1515

@@ -19,12 +19,12 @@ describe('hello world', function () {
1919
it('should greet', function() {
2020
browser.get(URL);
2121

22-
expect(getShadowText('hello-app')).toBe('hello world!');
22+
expect(getGreetingText('hello-app')).toBe('hello world!');
2323
});
2424
});
2525

2626
});
2727

28-
function getShadowText(selector) {
29-
return browser.executeScript('return document.querySelector("'+selector+'").shadowRoot.textContent');
30-
}
28+
function getGreetingText(selector) {
29+
return browser.executeScript('return document.querySelector("'+selector+'").shadowRoot.firstChild.textContent');
30+
}

modules/examples/src/hello_world/index_common.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import {bootstrap, Component, Decorator, TemplateConfig, NgElement} from 'core/c
2020
// The template for the component.
2121
// Expressions in the template (like {{greeting}}) are evaluated in the
2222
// context of the HelloCmp class below.
23-
inline: `{{greeting}} <span red>world</span>!`,
23+
inline: `<div>{{greeting}} <span red>world</span>!</div>
24+
<button (click)="changeGreeting()">change greeting</button>`,
2425
// All directives used in the template need to be specified. This allows for
2526
// modularity (RedDec can only be used in this template)
2627
// and better tooling (the template can be invalidated if the attribute is
@@ -33,6 +34,9 @@ class HelloCmp {
3334
constructor(service: GreetingService) {
3435
this.greeting = service.greeting;
3536
}
37+
changeGreeting() {
38+
this.greeting = 'howdy';
39+
}
3640
}
3741

3842
// Decorators are light-weight. They don't allow for templates, or new

modules/facade/src/dom.dart

+9-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,15 @@ class DOM {
3333
return el.querySelectorAll(selector);
3434
}
3535
static on(element, event, callback) {
36-
element.addEventListener(event, callback);
36+
// due to https://code.google.com/p/dart/issues/detail?id=17406
37+
// addEventListener misses zones so we use element.on.
38+
element.on[event].listen(callback);
39+
}
40+
static dispatchEvent(el, evt) {
41+
el.dispatchEvent(evt);
42+
}
43+
static createMouseEvent(eventType) {
44+
return new MouseEvent(eventType, canBubble: true);
3745
}
3846
static getInnerHTML(el) {
3947
return el.innerHtml;

modules/facade/src/dom.es6

+8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ export class DOM {
2424
static on(el, evt, listener) {
2525
el.addEventListener(evt, listener, false);
2626
}
27+
static dispatchEvent(el, evt) {
28+
el.dispatchEvent(evt);
29+
}
30+
static createMouseEvent(eventType) {
31+
var evt = new MouseEvent(eventType);
32+
evt.initEvent(eventType, true, true);
33+
return evt;
34+
}
2735
static getInnerHTML(el) {
2836
return el.innerHTML;
2937
}

0 commit comments

Comments
 (0)