@@ -212,7 +212,7 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
212212 return isPresent ( boundElementIndex ) ? this . elementRefs [ boundElementIndex ] : null ;
213213 }
214214
215- getDebugContext ( elementIndex : number , directiveIndex : DirectiveIndex ) : StringMap < string , any > {
215+ getDebugContext ( elementIndex : number , directiveIndex : DirectiveIndex ) : DebugContext {
216216 try {
217217 var offsettedIndex = this . elementOffset + elementIndex ;
218218 var hasRefForIndex = offsettedIndex < this . elementRefs . length ;
@@ -226,18 +226,13 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
226226 var directive = isPresent ( directiveIndex ) ? this . getDirectiveFor ( directiveIndex ) : null ;
227227 var injector = isPresent ( ei ) ? ei . getInjector ( ) : null ;
228228
229- return {
230- element : element ,
231- componentElement : componentElement ,
232- directive : directive ,
233- context : this . context ,
234- locals : _localsToStringMap ( this . locals ) ,
235- injector : injector
236- } ;
229+ return new DebugContext ( element , componentElement , directive , this . context ,
230+ _localsToStringMap ( this . locals ) , injector ) ;
231+
237232 } catch ( e ) {
238233 // TODO: vsavkin log the exception once we have a good way to log errors and warnings
239234 // if an error happens during getting the debug context, we return an empty map.
240- return { } ;
235+ return null ;
241236 }
242237 }
243238
@@ -262,29 +257,37 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
262257
263258 // returns false if preventDefault must be applied to the DOM event
264259 dispatchEvent ( boundElementIndex : number , eventName : string , locals : Map < string , any > ) : boolean {
265- // Most of the time the event will be fired only when the view is in the live document.
266- // However, in a rare circumstance the view might get dehydrated, in between the event
267- // queuing up and firing.
268- var allowDefaultBehavior = true ;
269- if ( this . hydrated ( ) ) {
270- var elBinder = this . proto . elementBinders [ boundElementIndex - this . elementOffset ] ;
271- if ( isBlank ( elBinder . hostListeners ) ) return allowDefaultBehavior ;
272- var eventMap = elBinder . hostListeners [ eventName ] ;
273- if ( isBlank ( eventMap ) ) return allowDefaultBehavior ;
274- MapWrapper . forEach ( eventMap , ( expr , directiveIndex ) => {
275- var context ;
276- if ( directiveIndex === - 1 ) {
277- context = this . context ;
278- } else {
279- context = this . elementInjectors [ boundElementIndex ] . getDirectiveAtIndex ( directiveIndex ) ;
280- }
281- var result = expr . eval ( context , new Locals ( this . locals , locals ) ) ;
282- if ( isPresent ( result ) ) {
283- allowDefaultBehavior = allowDefaultBehavior && result == true ;
284- }
285- } ) ;
260+ try {
261+ // Most of the time the event will be fired only when the view is in the live document.
262+ // However, in a rare circumstance the view might get dehydrated, in between the event
263+ // queuing up and firing.
264+ var allowDefaultBehavior = true ;
265+ if ( this . hydrated ( ) ) {
266+ var elBinder = this . proto . elementBinders [ boundElementIndex - this . elementOffset ] ;
267+ if ( isBlank ( elBinder . hostListeners ) ) return allowDefaultBehavior ;
268+ var eventMap = elBinder . hostListeners [ eventName ] ;
269+ if ( isBlank ( eventMap ) ) return allowDefaultBehavior ;
270+ MapWrapper . forEach ( eventMap , ( expr , directiveIndex ) => {
271+ var context ;
272+ if ( directiveIndex === - 1 ) {
273+ context = this . context ;
274+ } else {
275+ context = this . elementInjectors [ boundElementIndex ] . getDirectiveAtIndex ( directiveIndex ) ;
276+ }
277+ var result = expr . eval ( context , new Locals ( this . locals , locals ) ) ;
278+ if ( isPresent ( result ) ) {
279+ allowDefaultBehavior = allowDefaultBehavior && result == true ;
280+ }
281+ } ) ;
282+ }
283+ return allowDefaultBehavior ;
284+ } catch ( e ) {
285+ var c = this . getDebugContext ( boundElementIndex - this . elementOffset , null ) ;
286+ var context = isPresent ( c ) ? new _Context ( c . element , c . componentElement , c . context , c . locals ,
287+ c . injector ) :
288+ null ;
289+ throw new EventEvaluationError ( eventName , e , e . stack , context ) ;
286290 }
287- return allowDefaultBehavior ;
288291 }
289292}
290293
@@ -298,6 +301,29 @@ function _localsToStringMap(locals: Locals): StringMap<string, any> {
298301 return res ;
299302}
300303
304+ export class DebugContext {
305+ constructor ( public element : any , public componentElement : any , public directive : any ,
306+ public context : any , public locals : any , public injector : any ) { }
307+ }
308+
309+ /**
310+ * Error context included when an event handler throws an exception.
311+ */
312+ class _Context {
313+ constructor ( public element : any , public componentElement : any , public context : any ,
314+ public locals : any , public injector : any ) { }
315+ }
316+
317+ /**
318+ * Wraps an exception thrown by an event handler.
319+ */
320+ class EventEvaluationError extends BaseException {
321+ constructor ( eventName : string , originalException : any , originalStack : any , context : any ) {
322+ super ( `Error during evaluation of "${ eventName } "` , originalException , originalStack , context ) ;
323+ }
324+ }
325+
326+
301327/**
302328 *
303329 */
0 commit comments