feat(Directives): add the ability to declaratively bind events
relates to #621
This commit is contained in:
@ -19,9 +19,14 @@ import {UrlResolver} from 'angular2/src/core/compiler/url_resolver';
|
||||
import {StyleUrlResolver} from 'angular2/src/core/compiler/style_url_resolver';
|
||||
import {CssProcessor} from 'angular2/src/core/compiler/css_processor';
|
||||
|
||||
import {EventManager, DomEventsPlugin} from 'angular2/src/core/events/event_manager';
|
||||
|
||||
import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
|
||||
|
||||
import {Decorator, Component, Viewport} from 'angular2/src/core/annotations/annotations';
|
||||
import {Template} from 'angular2/src/core/annotations/template';
|
||||
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
|
||||
import {EventEmitter} from 'angular2/src/core/annotations/di';
|
||||
|
||||
import {If} from 'angular2/src/directives/if';
|
||||
|
||||
@ -57,7 +62,11 @@ export function main() {
|
||||
var view, ctx, cd;
|
||||
function createView(pv) {
|
||||
ctx = new MyComp();
|
||||
view = pv.instantiate(null, null, reflector);
|
||||
view = pv.instantiate(
|
||||
null,
|
||||
new EventManager([new DomEventsPlugin()], new FakeVmTurnZone()),
|
||||
reflector
|
||||
);
|
||||
view.hydrate(new Injector([]), null, ctx);
|
||||
cd = view.changeDetector;
|
||||
}
|
||||
@ -435,6 +444,32 @@ export function main() {
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
it('should support events', (done) => {
|
||||
tplResolver.setTemplate(MyComp, new Template({
|
||||
inline: '<div emitter listener></div>',
|
||||
directives: [DecoratorEmitingEvent, DecoratorListeningEvent]
|
||||
}));
|
||||
|
||||
compiler.compile(MyComp).then((pv) => {
|
||||
createView(pv);
|
||||
|
||||
var injector = view.elementInjectors[0];
|
||||
|
||||
var emitter = injector.get(DecoratorEmitingEvent);
|
||||
var listener = injector.get(DecoratorListeningEvent);
|
||||
|
||||
expect(emitter.msg).toEqual('');
|
||||
expect(listener.msg).toEqual('');
|
||||
|
||||
emitter.fireEvent('fired !');
|
||||
expect(emitter.msg).toEqual('fired !');
|
||||
expect(listener.msg).toEqual('fired !');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
if (assertionsEnabled()) {
|
||||
@ -651,3 +686,56 @@ class DoublePipeFactory {
|
||||
return new DoublePipe();
|
||||
}
|
||||
}
|
||||
|
||||
@Decorator({
|
||||
selector: '[emitter]',
|
||||
events: {'event': 'onEvent($event)'}
|
||||
})
|
||||
class DecoratorEmitingEvent {
|
||||
msg: string;
|
||||
emitter;
|
||||
|
||||
constructor(@EventEmitter('event') emitter:Function) {
|
||||
this.msg = '';
|
||||
this.emitter = emitter;
|
||||
}
|
||||
|
||||
fireEvent(msg: string) {
|
||||
this.emitter(msg);
|
||||
}
|
||||
|
||||
onEvent(msg: string) {
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
||||
|
||||
@Decorator({
|
||||
selector: '[listener]',
|
||||
events: {'event': 'onEvent($event)'}
|
||||
})
|
||||
class DecoratorListeningEvent {
|
||||
msg: string;
|
||||
|
||||
constructor() {
|
||||
this.msg = '';
|
||||
}
|
||||
|
||||
onEvent(msg: string) {
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
||||
|
||||
class FakeVmTurnZone extends VmTurnZone {
|
||||
constructor() {
|
||||
super({enableLongStackTrace: false});
|
||||
}
|
||||
|
||||
run(fn) {
|
||||
fn();
|
||||
}
|
||||
|
||||
runOutsideAngular(fn) {
|
||||
fn();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {describe, beforeEach, it, expect, iit, ddescribe, el} from 'angular2/test_lib';
|
||||
import {isPresent, normalizeBlank} from 'angular2/src/facade/lang';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {ListWrapper, MapWrapper, Map, StringMap, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {ElementBinderBuilder} from 'angular2/src/core/compiler/pipeline/element_binder_builder';
|
||||
import {CompilePipeline} from 'angular2/src/core/compiler/pipeline/compile_pipeline';
|
||||
@ -138,7 +138,6 @@ export function main() {
|
||||
expect(pv.elementBinders[1].protoElementInjector).toBeNull();
|
||||
});
|
||||
|
||||
|
||||
it('should store the component directive', () => {
|
||||
var directives = [SomeComponentDirective];
|
||||
var pipeline = createPipeline({protoElementInjector: null, directives: directives});
|
||||
@ -379,10 +378,30 @@ export function main() {
|
||||
var results = pipeline.process(el('<div viewroot event-binding></div>'));
|
||||
var pv = results[0].inheritedProtoView;
|
||||
|
||||
var ast = MapWrapper.get(pv.elementBinders[0].events, 'event1');
|
||||
var eventMap = StringMapWrapper.get(pv.elementBinders[0].events, 'event1');
|
||||
var ast = MapWrapper.get(eventMap, -1);
|
||||
expect(ast.eval(null)).toBe(2);
|
||||
});
|
||||
|
||||
it('should bind directive events', () => {
|
||||
var directives = [SomeDecoratorWithEvent];
|
||||
var protoElementInjector = new ProtoElementInjector(null, 0, directives, true);
|
||||
var pipeline = createPipeline({
|
||||
directives: directives,
|
||||
protoElementInjector: protoElementInjector
|
||||
});
|
||||
var results = pipeline.process(el('<div viewroot directives></div>'));
|
||||
var pv = results[0].inheritedProtoView;
|
||||
|
||||
var directiveEvents = pv.elementBinders[0].events;
|
||||
var eventMap = StringMapWrapper.get(directiveEvents, 'event');
|
||||
// Get the cb AST for the directive at index 0 (SomeDecoratorWithEvent)
|
||||
var ast = MapWrapper.get(eventMap, 0);
|
||||
|
||||
var context = new SomeDecoratorWithEvent();
|
||||
expect(ast.eval(context)).toEqual('onEvent() callback');
|
||||
});
|
||||
|
||||
it('should bind directive properties', () => {
|
||||
var propertyBindings = MapWrapper.createFromStringMap({
|
||||
'boundprop1': 'prop1',
|
||||
@ -516,6 +535,21 @@ class SomeDecoratorDirectiveWithBinding {
|
||||
}
|
||||
}
|
||||
|
||||
@Decorator({
|
||||
events: {'event': 'onEvent($event)'}
|
||||
})
|
||||
class SomeDecoratorWithEvent {
|
||||
// Added here so that we don't have to wrap the content in a ContextWithVariableBindings
|
||||
$event: string;
|
||||
|
||||
constructor() {
|
||||
this.$event = 'onEvent'
|
||||
}
|
||||
onEvent(event) {
|
||||
return `${event}() callback`;
|
||||
}
|
||||
}
|
||||
|
||||
@Decorator({
|
||||
bind: {
|
||||
'decorProp': 'boundprop1',
|
||||
|
26
modules/angular2/test/core/compiler/view_spec.js
vendored
26
modules/angular2/test/core/compiler/view_spec.js
vendored
@ -519,6 +519,20 @@ export function main() {
|
||||
|
||||
expect(called).toEqual(1);
|
||||
});
|
||||
|
||||
it('should bind to directive events', () => {
|
||||
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
|
||||
new DynamicProtoChangeDetector(null), null);
|
||||
pv.bindElement(new ProtoElementInjector(null, 0, [SomeDirectiveWithEventHandler]));
|
||||
pv.bindEvent('click', parser.parseAction('onEvent($event)', null), 0);
|
||||
view = createView(pv, new EventManager([new DomEventsPlugin()], new FakeVmTurnZone()));
|
||||
|
||||
var directive = view.elementInjectors[0].get(SomeDirectiveWithEventHandler);
|
||||
expect(directive.event).toEqual(null);
|
||||
|
||||
dispatchClick(view.nodes[0]);
|
||||
expect(directive.event).toBe(dispatchedEvent);
|
||||
});
|
||||
});
|
||||
|
||||
describe('react to record changes', () => {
|
||||
@ -690,7 +704,6 @@ class SomeViewport {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class AnotherDirective {
|
||||
prop:string;
|
||||
constructor() {
|
||||
@ -708,6 +721,17 @@ class EventEmitterDirective {
|
||||
}
|
||||
}
|
||||
|
||||
class SomeDirectiveWithEventHandler {
|
||||
event;
|
||||
|
||||
constructor() {
|
||||
this.event = null;
|
||||
}
|
||||
|
||||
onEvent(event) {
|
||||
this.event = event;
|
||||
}
|
||||
}
|
||||
|
||||
class MyEvaluationContext {
|
||||
foo:string;
|
||||
|
Reference in New Issue
Block a user