feat(view): adds event binding to view instantiation.

Only native non-bubbling events are supported by the commit.
This commit is contained in:
Rado Kirov 2015-01-12 11:50:54 -08:00
parent bccc863567
commit c5b0baf805
6 changed files with 75 additions and 7 deletions

View File

@ -373,6 +373,22 @@ export class ProtoView {
if (isPresent(elementInjector)) { if (isPresent(elementInjector)) {
preBuiltObjects[i] = new PreBuiltObjects(view, new NgElement(element), viewPort, lightDom); 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, view.init(elementInjectors, rootElementInjectors, textNodes, elementsWithPropertyBindings,

View File

@ -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('<div class="ng-binding"><div></div></div>'),
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', () => { describe('react to record changes', () => {
var view, cd, ctx; var view, cd, ctx;
@ -602,6 +633,7 @@ class MyEvaluationContext {
foo:string; foo:string;
a; a;
b; b;
callMe;
constructor() { constructor() {
this.foo = 'bar'; this.foo = 'bar';
}; };

View File

@ -9,7 +9,7 @@ describe('hello world', function () {
it('should greet', function() { it('should greet', function() {
browser.get(URL); 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() { it('should greet', function() {
browser.get(URL); browser.get(URL);
expect(getShadowText('hello-app')).toBe('hello world!'); expect(getGreetingText('hello-app')).toBe('hello world!');
}); });
}); });
}); });
function getShadowText(selector) { function getGreetingText(selector) {
return browser.executeScript('return document.querySelector("'+selector+'").shadowRoot.textContent'); return browser.executeScript('return document.querySelector("'+selector+'").shadowRoot.firstChild.textContent');
} }

View File

@ -20,7 +20,8 @@ import {bootstrap, Component, Decorator, TemplateConfig, NgElement} from 'core/c
// The template for the component. // The template for the component.
// Expressions in the template (like {{greeting}}) are evaluated in the // Expressions in the template (like {{greeting}}) are evaluated in the
// context of the HelloCmp class below. // context of the HelloCmp class below.
inline: `{{greeting}} <span red>world</span>!`, inline: `<div>{{greeting}} <span red>world</span>!</div>
<button (click)="changeGreeting()">change greeting</button>`,
// All directives used in the template need to be specified. This allows for // All directives used in the template need to be specified. This allows for
// modularity (RedDec can only be used in this template) // modularity (RedDec can only be used in this template)
// and better tooling (the template can be invalidated if the attribute is // and better tooling (the template can be invalidated if the attribute is
@ -33,6 +34,9 @@ class HelloCmp {
constructor(service: GreetingService) { constructor(service: GreetingService) {
this.greeting = service.greeting; this.greeting = service.greeting;
} }
changeGreeting() {
this.greeting = 'howdy';
}
} }
// Decorators are light-weight. They don't allow for templates, or new // Decorators are light-weight. They don't allow for templates, or new

View File

@ -33,7 +33,15 @@ class DOM {
return el.querySelectorAll(selector); return el.querySelectorAll(selector);
} }
static on(element, event, callback) { 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) { static getInnerHTML(el) {
return el.innerHtml; return el.innerHtml;

View File

@ -24,6 +24,14 @@ export class DOM {
static on(el, evt, listener) { static on(el, evt, listener) {
el.addEventListener(evt, listener, false); 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) { static getInnerHTML(el) {
return el.innerHTML; return el.innerHTML;
} }