creating jquery plugins with TypeScript

Topics: General
Oct 7, 2012 at 9:38 PM

How would one extend jquery with a custom plugin...

The recommended pattern for doing this, is according to the website of jQuery:
 

(function($) {
  $.fn.myPlugin = function() {
      ...
      return this.each( function() {  // maintain chainability
          var $this =$(this);
            ...
     });

  };
}) (jQuery);

What would be best practice code in TypeScript?

Oct 7, 2012 at 10:23 PM

You would create your plugin the way you normally do as above... Then you need to extend TypeScripts definition of JQuery to include your added plugin methods.  So for the above plugin its simply:

interface JQuery {
    myPlugin(): JQuery;
}

Now anyone that refernces both the standard jquery.d.ts file and your plugin.ts file will see your extended version of JQuery.  I posted a slightly more indepth example over on StackOverflow a few days ago.

 

Oct 8, 2012 at 8:09 AM

Thx.. got it working

Oct 12, 2012 at 6:59 PM

I have another question regarding this topic:

How is the right way to write a jquery plugin? I read somewhere about this pattern:

 

(function ($) {
    var methods = {
        init: function (options) {
            return this.each(function () {
                var $this = $(this),
                    data = $this.data("MyPlugin");
            });
        },
        nextMethod: function () { ... }
    };

    $.fn.MyPlugin = function (method) {
        // Method calling logic
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.MyPlugin');
        }
    };
})(jQuery);

 

 

How would you write this in Typescript. Would you suggest another pattern? I defined the following Interface to get it working:

interface JQuery {
    MyPlugin(...arguments: any[]): JQuery;
}
Obviously I wont get intellisense support für methods like "init" or "nextMethod". Please tell me, which is the right pattern for creating your own JQuery plugin.

Oct 14, 2012 at 7:35 PM

Here's one way to create a jQuery plugin in TypeScript that is a little more strongly typed.  I chose to use a module to create a closure around the plugins methods.  That lets you create global variable and such within the closure that can only be accessed by the plugin. 

The tricky bit with this of course is the plugin function intself.  jQuery wants you to have only a single plugin function that then dispatches calls to your plugins other methods.  This is to help avoid conflicts with other plugins but it makes strongly typing things difficult.  The real problem is that the this pointer of your plugin function gets set to the jQuery instance being invoked and that has to be passed to the method you're dispatching to.  In their example they simply call apply() on the invoked method using the 'this' of the jQuery object. I chose not to do that because TypeScript isn't going to know what that means so instead I pass the 'this' pointer to the invoked method as a stringly typed param which keeps TypeScript happy.

There's still the crappyness around having this weekly typed plugin method but the problem is you need something that binds the this pointer to the invoked plugin method.  I can think of one way to do this which would have you call your plugin with a syntax like "$('.foo').MyPlugin().helloWorld();" but there's some caching you'd have to do to keep things performant and I'd want to work up some code to ensure everything works...    

module MyPlugin {
	export function init($sel: JQuery, options: IOptions): JQuery {
        		return $sel.each(function () {
            		var $this = $(this),
                		data = $this.data("MyPlugin");
        		});
	}
	
	export function helloWorld($sel: JQuery): string {
		return 'hello world';
	}

	export interface IOptions {
		
	}
}


(function ($) {
    $.fn.MyPlugin = function (method, ...args: any[]) {
        // Method calling logic
	if (MyPlugin[method]) {
            	return MyPlugin[method].apply(MyPlugin, [this].concat(args));
        	} else if (typeof method === 'object' || !method) {
            	return MyPlugin.init.call(MyPlugin, this, arguments[0]);
        	} else {
            	$.error('Method ' + method + ' does not exist on jQuery.MyPlugin');
        	}
    };
})(jQuery);