parliament
parliament

Reputation: 22934

Typescript event binding and unbinding

I asked this question a while ago about delegating jquery events to event handler methods in a typescript class. The answer was to use the following pattern

public class LoginDialog { 
     ...
     constructor() { 
        //this.open = this.OpenHandler; //Incorrect way
        this.open = (event: Event, ui: DialogUIParams) => {  //Correct way
             this.OpenHandler(this, event, ui);
        }; 
        ...
     }

     public OpenHandler(context: LoginDialog, event: Event, ui: DialogUIParams) { 
           //Use context as "this"
     } 
     ...
 } 

However, now I'm at a similar situation where I need to remove the handler at some time in the future but am unable to because of the anonymous function:

public class LoginDialog { 
     ...
     constructor() { 
        this.open = (event: Event, ui: DialogUIParams) => {  
             this.OpenHandler(this, event, ui);
        }; 
        this.close = (event: Event, ui: DialogUIParams) => {
             this.CloseHandler(this, event, ui);
        }
        ...
     }

     public OpenHandler(context: LoginDialog, event: Event, ui: DialogUIParams) { 
           $(window).on("scroll", () => { context.ScrollHandler(context); });
           //$(window).on("scroll", context.ScrollHandler(context)); //Never gets called
           //$(window).on("scroll", context.ScrollHandler); //this = window
     } 

     public CloseHandler(context: LoginDialog, event: Event, ui: DialogUIParams) { 
           $(window).off("scroll", () => { context.ScrollHandler(context); }); //Does not remove
     } 

     public ScrollHandler(context: Interfaces.IBaseDialog) { 
           context.jQueryDialog.dialog("option", "position", "center");
           ...
     }
     ...
 } 

In this case without the arrow syntax in the on binding, my ResizeOrScrollHandler never get's called, but with it I'm unable to remove the event handler in the CloseHandler

Upvotes: 4

Views: 10184

Answers (1)

Judah Gabriel Himango
Judah Gabriel Himango

Reputation: 60041

To summarize your problem, you're trying to use jQuery to hook (and unhook) an event on a DOM element, but you want the event handler's 'this' context to point to the class containing the event handler.

There are multiple ways to solve this.

First, you can use jQuery.proxy

class LoginDialog {

   public OpenHandler(context: LoginDialog, event: Event, ui: DialogUIParams) { 
      $(window).on("scroll", $.proxy(context.ScrollHandler, context));
   }    

   public CloseHandler(context: LoginDialog, event: Event, ui: DialogUIParams) {
      $(window).off("scroll", context.ScrollHandler);
   }
}

Another option would be to use RxJS, which allows you to treat events like disposable objects:

var scrollSubscription = $(window)
     .onAsObservable("scroll")
     .subscribe(() => console.log("this == the containing class!"));
...
// When we're done, just call .dispose.
scrollSubscription.dispose();

Have fun.

Upvotes: 6

Related Questions