parent
aabe83cf63
commit
883e1c1541
|
@ -514,7 +514,8 @@ Where:
|
|||
* `some-element` Any element which can generate DOM events (or has an angular directive which generates the event).
|
||||
* `some-event` (escaped with `()` or `bind-`) is the name of the event `some-event`. In this case the
|
||||
dash-case is converted into camel-case `someEvent`.
|
||||
* `statement` is a valid statement (as defined in section below).
|
||||
* `statement` is a valid statement (as defined in section below).
|
||||
If the execution of the statement returns `false`, then `preventDefault`is applied on the DOM event.
|
||||
|
||||
By default, angular only listens to the element on the event, and ignores events which bubble. To listen to bubbled
|
||||
events (as in the case of clicking on any child) use the bubble option (`(^event)` or `on-bubble-event`) as shown
|
||||
|
|
|
@ -391,6 +391,7 @@ export class Directive extends Injectable {
|
|||
*
|
||||
* - `event1`: the DOM event that the directive listens to.
|
||||
* - `statement`: the statement to execute when the event occurs.
|
||||
* If the evalutation of the statement returns `false`, then `preventDefault`is applied on the DOM event.
|
||||
*
|
||||
* To listen to global events, a target must be added to the event name.
|
||||
* The target can be `window`, `document` or `body`.
|
||||
|
|
|
@ -131,17 +131,17 @@ export class AppView {
|
|||
}
|
||||
|
||||
// implementation of EventDispatcher#dispatchEvent
|
||||
dispatchEvent(
|
||||
elementIndex:number, eventName:string, locals:Map<string, any>
|
||||
):void {
|
||||
// returns false if preventDefault must be applied to the DOM event
|
||||
dispatchEvent(elementIndex:number, eventName:string, locals:Map<string, any>): boolean {
|
||||
// Most of the time the event will be fired only when the view is in the live document.
|
||||
// However, in a rare circumstance the view might get dehydrated, in between the event
|
||||
// queuing up and firing.
|
||||
var allowDefaultBehavior = true;
|
||||
if (this.hydrated()) {
|
||||
var elBinder = this.proto.elementBinders[elementIndex];
|
||||
if (isBlank(elBinder.hostListeners)) return;
|
||||
if (isBlank(elBinder.hostListeners)) return allowDefaultBehavior;
|
||||
var eventMap = elBinder.hostListeners[eventName];
|
||||
if (isBlank(eventMap)) return;
|
||||
if (isBlank(eventMap)) return allowDefaultBehavior;
|
||||
MapWrapper.forEach(eventMap, (expr, directiveIndex) => {
|
||||
var context;
|
||||
if (directiveIndex === -1) {
|
||||
|
@ -149,9 +149,13 @@ export class AppView {
|
|||
} else {
|
||||
context = this.elementInjectors[elementIndex].getDirectiveAtIndex(directiveIndex);
|
||||
}
|
||||
expr.eval(context, new Locals(this.locals, locals));
|
||||
var result = expr.eval(context, new Locals(this.locals, locals));
|
||||
if (isPresent(result)) {
|
||||
allowDefaultBehavior = allowDefaultBehavior && result;
|
||||
}
|
||||
});
|
||||
}
|
||||
return allowDefaultBehavior;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,7 +84,8 @@ export class RenderView {
|
|||
this._eventDispatcher = dispatcher;
|
||||
}
|
||||
|
||||
dispatchEvent(elementIndex, eventName, event) {
|
||||
dispatchEvent(elementIndex, eventName, event): boolean {
|
||||
var allowDefaultBehavior = true;
|
||||
if (isPresent(this._eventDispatcher)) {
|
||||
var evalLocals = MapWrapper.create();
|
||||
MapWrapper.set(evalLocals, '$event', event);
|
||||
|
@ -92,7 +93,11 @@ export class RenderView {
|
|||
// out of action expressions
|
||||
// var localValues = this.proto.elementBinders[elementIndex].eventLocals.eval(null, new Locals(null, evalLocals));
|
||||
// this._eventDispatcher.dispatchEvent(elementIndex, eventName, localValues);
|
||||
this._eventDispatcher.dispatchEvent(elementIndex, eventName, evalLocals);
|
||||
allowDefaultBehavior = this._eventDispatcher.dispatchEvent(elementIndex, eventName, evalLocals);
|
||||
if (!allowDefaultBehavior) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
return allowDefaultBehavior;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -591,6 +591,23 @@ export function main() {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should support preventing default on render events', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(MyComp, new View({
|
||||
template: '<input type="checkbox" listenerprevent></input><input type="checkbox" listenernoprevent></input>',
|
||||
directives: [DecoratorListeningDomEventPrevent, DecoratorListeningDomEventNoPrevent]
|
||||
}));
|
||||
|
||||
tb.createView(MyComp, {context: ctx}).then((view) => {
|
||||
expect(DOM.getChecked(view.rootNodes[0])).toBeFalsy();
|
||||
expect(DOM.getChecked(view.rootNodes[1])).toBeFalsy();
|
||||
DOM.dispatchEvent(view.rootNodes[0], DOM.createMouseEvent('click'));
|
||||
DOM.dispatchEvent(view.rootNodes[1], DOM.createMouseEvent('click'));
|
||||
expect(DOM.getChecked(view.rootNodes[0])).toBeFalsy();
|
||||
expect(DOM.getChecked(view.rootNodes[1])).toBeTruthy();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support render global events from multiple directives', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(MyComp, new View({
|
||||
template: '<div *if="ctxBoolProp" listener listenerother></div>',
|
||||
|
@ -1162,6 +1179,30 @@ class DecoratorListeningDomEventOther {
|
|||
}
|
||||
}
|
||||
|
||||
@Decorator({
|
||||
selector: '[listenerprevent]',
|
||||
hostListeners: {
|
||||
'click': 'onEvent($event)'
|
||||
}
|
||||
})
|
||||
class DecoratorListeningDomEventPrevent {
|
||||
onEvent(event) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Decorator({
|
||||
selector: '[listenernoprevent]',
|
||||
hostListeners: {
|
||||
'click': 'onEvent($event)'
|
||||
}
|
||||
})
|
||||
class DecoratorListeningDomEventNoPrevent {
|
||||
onEvent(event) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: '[id]',
|
||||
properties: {'id': 'id'}
|
||||
|
|
Loading…
Reference in New Issue