Search code examples
javascriptmodule-pattern

Javascript Module Pattern Events and Listeners


I'm implementing the module pattern, and would like to know the best/preferred way to define and register event listeners/handlers. The following works, but maybe there is a better/simpler way...

var  MODULE = function() {

    //  private
    var _field1;
    var _field2;

    function  localFunc(p) {
        alert('localFunc');
    }

    //  public
    return {
        // properties
        prop1: _field1,

        // events
        myEvent1Handler: {},
        myEvent1: function() {myEvent1Handler();},
        myEvent2Handler: {},
        myEvent2: function() {myEvent2Handler();},

        addListener: function  (event,func) {
            if (event  ==  "myEvent1")
                myEvent1Handler = func;   

            if (event  ==  "myEvent2")
                myEvent2Handler = func;      
        },

        // public  methods
        method1: function (p) {
            alert('method1 says:' + p);
            MODULE.myEvent1();
        },
        method2: function  (p) {
             alert('method2 doing  stuff');
             localFunc(p);
            MODULE.myEvent2();
        }

    };
}();

// register for events
MODULE.addListener("myEvent1",function(){alert('fired1');});  
MODULE.addListener("myEvent2",function(){alert('fired2');});  

// use module (only event1 should fire!)
MODULE.method1("hello");  

Try it out:

http://jsfiddle.net/RqusH/3/

Seems like a lot of work to have myEventx, myEventHandlerx, and addListener?


Solution

  • Normally, I wouldn't respond to an architectural question with a specific implementation (especially one dependent on a 3rd-party library like jQuery), but since my implementation promotes reusability — and we are talking patterns here — I will present a small jQuery plugin, $.eventable, which augments JavaScript objects with jQuery's event methods.

    This plugin will allow you to implement event handling capability on any object (or class instance) with one simple call.

    jQuery.eventable = function (obj) {
      // Allow use of Function.prototype for shorthanding the augmentation of classes
      obj = jQuery.isFunction(obj) ? obj.prototype : obj;
      // Augment the object (or prototype) with eventable methods
      return $.extend(obj, jQuery.eventable.prototype);
    };
    
    jQuery.eventable.prototype = {
    
      // The trigger event must be augmented separately because it requires a
      // new Event to prevent unexpected triggering of a method (and possibly
      // infinite recursion) when the event type matches the method name
      trigger: function (type, data) {
        var event = new jQuery.Event(type); 
        event.preventDefault();                
        jQuery.event.trigger(event, data, this);
        return this;
      }
    };
    
    // Augment the object with jQuery's event methods
    jQuery.each(['bind', 'one', 'unbind', 'on', 'off'], function (i, method) {
      jQuery.eventable.prototype[method] = function (type, data, fn) {
        jQuery(this)[method](type, data, fn);
        return this;
      };
    });
    

    If you include that snippet, you can implement your solution like this:

    var MODULE = function() {
    
      //  private
      var _field1;
      var _field2;
    
      function localFunc(p) {
        alert('localFunc');
      }
    
      //  public
      return $.eventable({
    
        // properties
        prop1: _field1,
    
        // public  methods
        method1: function(p) {
          alert('method1 says:' + p);
          this.trigger('myEvent1');
        },
    
        method2: function(p) {
          alert('method2 doing  stuff');
          localFunc(p);
          this.trigger('myEvent2');
        }
    
      });
    } ();
    
    // register for events
    MODULE.on("myEvent1", function() {
      alert('fired1');
    });
    MODULE.on("myEvent2", function() {
      alert('fired2');
    });
    
    // use module (only event1 should fire!)
    MODULE.method1("hello");
    

    Your MODULE now has the following callable methods:

    MODULE.on(event, /* data, */ handler);
    MODULE.bind(event, /* data, */ handler);
    MODULE.one(event, /* data ,*/ handler);
    MODULE.off(event, handler);
    MODULE.unbind(event, handler);
    MODULE.trigger(event /*, data */);
    

    Where event is a space-delimited list of events, handler is your callback, and data is an optional value to pass to your callbacks.

    You can refer to jQuery's documentation for more details.