feat(events): adds support for injectable angular event emitters.
Event emitters can be injected into Directives. Event emitters take over browser events with the same name. Emitted events do not bubble. Event emitters can be injected even if there is no corresponding callback in the template. Use as follows: @Decorator(...) class MyDec(@EventEmitter('click') clickEmitter) { ... fireClick() { var eventData = {...}; this._clickEmitter(eventData); } }
This commit is contained in:
@ -3,6 +3,7 @@ import {isBlank, isPresent, FIELD, IMPLEMENTS, proxy} from 'facade/lang';
|
||||
import {ListWrapper, MapWrapper, List} from 'facade/collection';
|
||||
import {ProtoElementInjector, PreBuiltObjects, DirectiveBinding} from 'core/compiler/element_injector';
|
||||
import {Parent, Ancestor} from 'core/annotations/visibility';
|
||||
import {EventEmitter} from 'core/annotations/events';
|
||||
import {onDestroy} from 'core/annotations/annotations';
|
||||
import {Injector, Inject, bind} from 'di/di';
|
||||
import {View} from 'core/compiler/view';
|
||||
@ -56,6 +57,16 @@ class NeedsService {
|
||||
}
|
||||
}
|
||||
|
||||
class NeedsEventEmitter {
|
||||
clickEmitter;
|
||||
constructor(@EventEmitter('click') clickEmitter:Function) {
|
||||
this.clickEmitter = clickEmitter;
|
||||
}
|
||||
click() {
|
||||
this.clickEmitter(null);
|
||||
}
|
||||
}
|
||||
|
||||
class A_Needs_B {
|
||||
constructor(dep){}
|
||||
}
|
||||
@ -100,7 +111,7 @@ export function main() {
|
||||
if (isBlank(lightDomAppInjector)) lightDomAppInjector = new Injector([]);
|
||||
|
||||
var proto = new ProtoElementInjector(null, 0, bindings, isPresent(shadowDomAppInjector));
|
||||
var inj = proto.instantiate(null, null);
|
||||
var inj = proto.instantiate(null, null, null);
|
||||
var preBuilt = isPresent(preBuiltObjects) ? preBuiltObjects : defaultPreBuiltObjects;
|
||||
|
||||
inj.instantiateDirectives(lightDomAppInjector, shadowDomAppInjector, preBuilt);
|
||||
@ -113,12 +124,12 @@ export function main() {
|
||||
var inj = new Injector([]);
|
||||
|
||||
var protoParent = new ProtoElementInjector(null, 0, parentBindings);
|
||||
var parent = protoParent.instantiate(null, null);
|
||||
var parent = protoParent.instantiate(null, null, null);
|
||||
|
||||
parent.instantiateDirectives(inj, null, parentPreBuildObjects);
|
||||
|
||||
var protoChild = new ProtoElementInjector(protoParent, 1, childBindings, false, 1);
|
||||
var child = protoChild.instantiate(parent, null);
|
||||
var child = protoChild.instantiate(parent, null, null);
|
||||
child.instantiateDirectives(inj, null, defaultPreBuiltObjects);
|
||||
|
||||
return child;
|
||||
@ -131,11 +142,11 @@ export function main() {
|
||||
var shadowInj = inj.createChild([]);
|
||||
|
||||
var protoParent = new ProtoElementInjector(null, 0, hostBindings, true);
|
||||
var host = protoParent.instantiate(null, null);
|
||||
var host = protoParent.instantiate(null, null, null);
|
||||
host.instantiateDirectives(inj, shadowInj, hostPreBuildObjects);
|
||||
|
||||
var protoChild = new ProtoElementInjector(protoParent, 0, shadowBindings, false, 1);
|
||||
var shadow = protoChild.instantiate(null, host);
|
||||
var shadow = protoChild.instantiate(null, host, null);
|
||||
shadow.instantiateDirectives(shadowInj, null, null);
|
||||
|
||||
return shadow;
|
||||
@ -148,9 +159,9 @@ export function main() {
|
||||
var protoChild1 = new ProtoElementInjector(protoParent, 1, []);
|
||||
var protoChild2 = new ProtoElementInjector(protoParent, 2, []);
|
||||
|
||||
var p = protoParent.instantiate(null, null);
|
||||
var c1 = protoChild1.instantiate(p, null);
|
||||
var c2 = protoChild2.instantiate(p, null);
|
||||
var p = protoParent.instantiate(null, null, null);
|
||||
var c1 = protoChild1.instantiate(p, null, null);
|
||||
var c2 = protoChild2.instantiate(p, null, null);
|
||||
|
||||
expect(humanize(p, [
|
||||
[p, 'parent'],
|
||||
@ -165,8 +176,8 @@ export function main() {
|
||||
var protoParent = new ProtoElementInjector(null, 0, []);
|
||||
var protoChild = new ProtoElementInjector(protoParent, 1, [], false, distance);
|
||||
|
||||
var p = protoParent.instantiate(null, null);
|
||||
var c = protoChild.instantiate(p, null);
|
||||
var p = protoParent.instantiate(null, null, null);
|
||||
var c = protoChild.instantiate(p, null, null);
|
||||
|
||||
expect(c.directParent()).toEqual(p);
|
||||
});
|
||||
@ -176,8 +187,8 @@ export function main() {
|
||||
var protoParent = new ProtoElementInjector(null, 0, []);
|
||||
var protoChild = new ProtoElementInjector(protoParent, 1, [], false, distance);
|
||||
|
||||
var p = protoParent.instantiate(null, null);
|
||||
var c = protoChild.instantiate(p, null);
|
||||
var p = protoParent.instantiate(null, null, null);
|
||||
var c = protoChild.instantiate(p, null, null);
|
||||
|
||||
expect(c.directParent()).toEqual(null);
|
||||
});
|
||||
@ -400,5 +411,18 @@ export function main() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('event emitters', () => {
|
||||
it('should be injectable and callable', () => {
|
||||
var inj = injector([NeedsEventEmitter]);
|
||||
inj.get(NeedsEventEmitter).click();
|
||||
});
|
||||
|
||||
it('should be queryable through hasEventEmitter', () => {
|
||||
var inj = injector([NeedsEventEmitter]);
|
||||
expect(inj.hasEventEmitter('click')).toBe(true);
|
||||
expect(inj.hasEventEmitter('move')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import {Component, Decorator, Template} from 'core/annotations/annotations';
|
||||
import {OnChange} from 'core/core';
|
||||
import {Lexer, Parser, ProtoChangeDetector, ChangeDetector} from 'change_detection/change_detection';
|
||||
import {TemplateConfig} from 'core/annotations/template_config';
|
||||
import {EventEmitter} from 'core/annotations/events';
|
||||
import {List, MapWrapper} from 'facade/collection';
|
||||
import {DOM, Element} from 'facade/dom';
|
||||
import {int, proxy, IMPLEMENTS} from 'facade/lang';
|
||||
@ -233,7 +234,7 @@ export function main() {
|
||||
pv.bindElement(testProtoElementInjector);
|
||||
|
||||
var hostProtoInjector = new ProtoElementInjector(null, 0, []);
|
||||
var hostInjector = hostProtoInjector.instantiate(null, null);
|
||||
var hostInjector = hostProtoInjector.instantiate(null, null, null);
|
||||
var view;
|
||||
expect(() => view = pv.instantiate(hostInjector)).not.toThrow();
|
||||
expect(testProtoElementInjector.parentElementInjector).toBe(view.elementInjectors[0]);
|
||||
@ -248,7 +249,7 @@ export function main() {
|
||||
pv.bindElement(testProtoElementInjector);
|
||||
|
||||
var hostProtoInjector = new ProtoElementInjector(null, 0, []);
|
||||
var hostInjector = hostProtoInjector.instantiate(null, null);
|
||||
var hostInjector = hostProtoInjector.instantiate(null, null, null);
|
||||
expect(() => pv.instantiate(hostInjector)).not.toThrow();
|
||||
expect(testProtoElementInjector.parentElementInjector).toBeNull();
|
||||
expect(testProtoElementInjector.hostElementInjector).toBe(hostInjector);
|
||||
@ -453,9 +454,30 @@ export function main() {
|
||||
createViewAndContext(createProtoView());
|
||||
|
||||
view.dehydrate();
|
||||
dispatchClick(view.nodes[0]);
|
||||
expect(() => dispatchClick(view.nodes[0])).not.toThrow();
|
||||
expect(called).toEqual(0);
|
||||
});
|
||||
|
||||
it('should support custom event emitters', () => {
|
||||
var pv = new ProtoView(el('<div class="ng-binding"><div></div></div>'),
|
||||
new ProtoChangeDetector());
|
||||
pv.bindElement(new TestProtoElementInjector(null, 0, [EventEmitterDirective]));
|
||||
pv.bindEvent('click', parser.parseBinding('callMe(\$event)', null));
|
||||
|
||||
createViewAndContext(pv);
|
||||
var dir = view.elementInjectors[0].get(EventEmitterDirective);
|
||||
|
||||
var dispatchedEvent = new Object();
|
||||
|
||||
dir.click(dispatchedEvent);
|
||||
expect(receivedEvent).toBe(dispatchedEvent);
|
||||
expect(called).toEqual(1);
|
||||
|
||||
// Should not eval the binding, because custom emitter takes over.
|
||||
dispatchClick(view.nodes[0]);
|
||||
|
||||
expect(called).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('react to record changes', () => {
|
||||
@ -634,6 +656,17 @@ class AnotherDirective {
|
||||
}
|
||||
}
|
||||
|
||||
class EventEmitterDirective {
|
||||
_clicker:Function;
|
||||
constructor(@EventEmitter('click') clicker:Function) {
|
||||
this._clicker = clicker;
|
||||
}
|
||||
click(eventData) {
|
||||
this._clicker(eventData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MyEvaluationContext {
|
||||
foo:string;
|
||||
a;
|
||||
@ -652,9 +685,9 @@ class TestProtoElementInjector extends ProtoElementInjector {
|
||||
super(parent, index, bindings, firstBindingIsComponent);
|
||||
}
|
||||
|
||||
instantiate(parent:ElementInjector, host:ElementInjector):ElementInjector {
|
||||
instantiate(parent:ElementInjector, host:ElementInjector, events):ElementInjector {
|
||||
this.parentElementInjector = parent;
|
||||
this.hostElementInjector = host;
|
||||
return super.instantiate(parent, host);
|
||||
return super.instantiate(parent, host, events);
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ export function main() {
|
||||
var insertionElement = dom.childNodes[1];
|
||||
parentView = createView([dom.childNodes[0]]);
|
||||
protoView = new ProtoView(el('<div>hi</div>'), new ProtoChangeDetector());
|
||||
elementInjector = new ElementInjector(null, null, null);
|
||||
elementInjector = new ElementInjector(null, null, null, null);
|
||||
viewPort = new ViewPort(parentView, insertionElement, protoView, elementInjector);
|
||||
customViewWithOneNode = createView([el('<div>single</div>')]);
|
||||
customViewWithTwoNodes = createView([el('<div>one</div>'), el('<div>two</div>')]);
|
||||
|
Reference in New Issue
Block a user