feat(core): remove the (^ syntax and make all DOM events bubbling

BREAKING CHANGE

Before
<div (^click)="onEventHandler()">
  <button></button>
</div>

After
<div (click)="onEventHandler()">
  <button></button>
</div>

Closes #3864
This commit is contained in:
vsavkin
2015-09-01 08:52:54 -07:00
committed by Victor Savkin
parent 9934b3ec7f
commit 60ce884671
22 changed files with 113 additions and 243 deletions
@@ -856,7 +856,9 @@ export function main() {
dispatchEvent(tc.nativeElement, 'domEvent');
expect(listener.eventType).toEqual('domEvent');
expect(listener.eventTypes)
.toEqual(
['domEvent', 'body_domEvent', 'document_domEvent', 'window_domEvent']);
async.done();
});
@@ -874,16 +876,16 @@ export function main() {
var tc = rootTC.componentViewChildren[0];
var listener = tc.inject(DirectiveListeningDomEvent);
dispatchEvent(DOM.getGlobalEventTarget("window"), 'domEvent');
expect(listener.eventType).toEqual('window_domEvent');
expect(listener.eventTypes).toEqual(['window_domEvent']);
listener = tc.inject(DirectiveListeningDomEvent);
listener.eventTypes = [];
dispatchEvent(DOM.getGlobalEventTarget("document"), 'domEvent');
expect(listener.eventType).toEqual('document_domEvent');
expect(listener.eventTypes).toEqual(['document_domEvent', 'window_domEvent']);
rootTC.destroy();
listener = tc.inject(DirectiveListeningDomEvent);
listener.eventTypes = [];
dispatchEvent(DOM.getGlobalEventTarget("body"), 'domEvent');
expect(listener.eventType).toEqual('');
expect(listener.eventTypes).toEqual([]);
async.done();
});
@@ -983,7 +985,7 @@ export function main() {
var listener = tc.inject(DirectiveListeningDomEvent);
var listenerother = tc.inject(DirectiveListeningDomEventOther);
dispatchEvent(DOM.getGlobalEventTarget("window"), 'domEvent');
expect(listener.eventType).toEqual('window_domEvent');
expect(listener.eventTypes).toEqual(['window_domEvent']);
expect(listenerother.eventType).toEqual('other_domEvent');
expect(globalCounter).toEqual(1);
@@ -1851,12 +1853,11 @@ class DirectiveListeningEvent {
})
@Injectable()
class DirectiveListeningDomEvent {
eventType: string;
constructor() { this.eventType = ''; }
onEvent(eventType: string) { this.eventType = eventType; }
onWindowEvent(eventType: string) { this.eventType = "window_" + eventType; }
onDocumentEvent(eventType: string) { this.eventType = "document_" + eventType; }
onBodyEvent(eventType: string) { this.eventType = "body_" + eventType; }
eventTypes: string[] = [];
onEvent(eventType: string) { this.eventTypes.push(eventType); }
onWindowEvent(eventType: string) { this.eventTypes.push("window_" + eventType); }
onDocumentEvent(eventType: string) { this.eventTypes.push("document_" + eventType); }
onBodyEvent(eventType: string) { this.eventTypes.push("body_" + eventType); }
}
var globalCounter = 0;
@@ -182,20 +182,6 @@ export function main() {
expect(eventBinding.fullName).toEqual('click');
});
it('should detect onbubble- syntax', () => {
var results = process(el('<div onbubble-click="b()"></div>'));
var eventBinding = results[0].eventBindings[0];
expect(eventBinding.source.source).toEqual('b()');
expect(eventBinding.fullName).toEqual('^click');
});
it('should detect onbubble- syntax with data- prefix', () => {
var results = process(el('<div data-onbubble-click="b()"></div>'));
var eventBinding = results[0].eventBindings[0];
expect(eventBinding.source.source).toEqual('b()');
expect(eventBinding.fullName).toEqual('^click');
});
it('should parse event handlers using on- syntax as actions', () => {
var results = process(el('<div on-click="foo=bar"></div>'));
var eventBinding = results[0].eventBindings[0];
@@ -31,16 +31,7 @@ export function main() {
var plugin = new FakeEventManagerPlugin(['click']);
var manager = new EventManager([plugin, domEventPlugin], new FakeNgZone());
manager.addEventListener(element, 'click', handler);
expect(plugin._nonBubbleEventHandlers.get('click')).toBe(handler);
});
it('should delegate bubbling events to plugins', () => {
var element = el('<div></div>');
var handler = (e) => e;
var plugin = new FakeEventManagerPlugin(['click']);
var manager = new EventManager([plugin, domEventPlugin], new FakeNgZone());
manager.addEventListener(element, '^click', handler);
expect(plugin._bubbleEventHandlers.get('click')).toBe(handler);
expect(plugin._eventHandler.get('click')).toBe(handler);
});
it('should delegate event bindings to the first plugin supporting the event', () => {
@@ -52,10 +43,10 @@ export function main() {
var manager = new EventManager([plugin1, plugin2], new FakeNgZone());
manager.addEventListener(element, 'click', clickHandler);
manager.addEventListener(element, 'dblclick', dblClickHandler);
expect(plugin1._nonBubbleEventHandlers.has('click')).toBe(false);
expect(plugin2._nonBubbleEventHandlers.get('click')).toBe(clickHandler);
expect(plugin2._nonBubbleEventHandlers.has('dblclick')).toBe(false);
expect(plugin1._nonBubbleEventHandlers.get('dblclick')).toBe(dblClickHandler);
expect(plugin1._eventHandler.has('click')).toBe(false);
expect(plugin2._eventHandler.get('click')).toBe(clickHandler);
expect(plugin2._eventHandler.has('dblclick')).toBe(false);
expect(plugin1._eventHandler.get('dblclick')).toBe(dblClickHandler);
});
it('should throw when no plugin can handle the event', () => {
@@ -66,20 +57,7 @@ export function main() {
.toThrowError('No event manager plugin found for event click');
});
it('by default events are only caught on same element', () => {
var element = el('<div><div></div></div>');
var child = DOM.firstChild(element);
var dispatchedEvent = DOM.createMouseEvent('click');
var receivedEvent = null;
var handler = (e) => { receivedEvent = e; };
var manager = new EventManager([domEventPlugin], new FakeNgZone());
manager.addEventListener(element, 'click', handler);
DOM.dispatchEvent(child, dispatchedEvent);
expect(receivedEvent).toBe(null);
});
it('bubbled events are caught when fired from a child', () => {
it('events are caught when fired from a child', () => {
var element = el('<div><div></div></div>');
// Workaround for https://bugs.webkit.org/show_bug.cgi?id=122755
DOM.appendChild(DOM.defaultDoc().body, element);
@@ -89,13 +67,13 @@ export function main() {
var receivedEvent = null;
var handler = (e) => { receivedEvent = e; };
var manager = new EventManager([domEventPlugin], new FakeNgZone());
manager.addEventListener(element, '^click', handler);
manager.addEventListener(element, 'click', handler);
DOM.dispatchEvent(child, dispatchedEvent);
expect(receivedEvent).toBe(dispatchedEvent);
});
it('should add and remove global event listeners with correct bubbling', () => {
it('should add and remove global event listeners', () => {
var element = el('<div><div></div></div>');
DOM.appendChild(DOM.defaultDoc().body, element);
var dispatchedEvent = DOM.createMouseEvent('click');
@@ -103,7 +81,7 @@ export function main() {
var handler = (e) => { receivedEvent = e; };
var manager = new EventManager([domEventPlugin], new FakeNgZone());
var remover = manager.addGlobalEventListener("document", '^click', handler);
var remover = manager.addGlobalEventListener("document", 'click', handler);
DOM.dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(dispatchedEvent);
@@ -111,37 +89,19 @@ export function main() {
remover();
DOM.dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(null);
remover = manager.addGlobalEventListener("document", 'click', handler);
DOM.dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(null);
});
});
}
class FakeEventManagerPlugin extends EventManagerPlugin {
_supports: string[];
_nonBubbleEventHandlers: Map<string, Function>;
_bubbleEventHandlers: Map<string, Function>;
constructor(supports: string[]) {
super();
this._supports = supports;
this._nonBubbleEventHandlers = new Map();
this._bubbleEventHandlers = new Map();
}
_eventHandler: Map<string, Function> = new Map();
constructor(public _supports: string[]) { super(); }
supports(eventName: string): boolean { return ListWrapper.contains(this._supports, eventName); }
addEventListener(element, eventName: string, handler: Function, shouldSupportBubble: boolean) {
if (shouldSupportBubble) {
this._bubbleEventHandlers.set(eventName, handler);
} else {
this._nonBubbleEventHandlers.set(eventName, handler);
}
return () => {
MapWrapper.delete(
shouldSupportBubble ? this._bubbleEventHandlers : this._nonBubbleEventHandlers, eventName)
};
addEventListener(element, eventName: string, handler: Function) {
this._eventHandler.set(eventName, handler);
return () => { MapWrapper.delete(this._eventHandler, eventName) };
}
}
@@ -65,7 +65,7 @@ export function main() {
.then((testComponent) => {
testComponent.detectChanges();
// TODO: shouldn't this be just 'click' rather than '^click'?
testComponent.query(By.css('a')).triggerEventHandler('^click', null);
testComponent.query(By.css('a')).triggerEventHandler('click', null);
expect(router.spy('navigateInstruction')).toHaveBeenCalledWith(dummyInstruction);
async.done();
});