简体   繁体   中英

Converting a jQuery plugin to TypeScript

Ok so first off here is my very basic jQuery plugin

(function ($){
    $.fn.greenify = function (options) {
        var settings = $.extend({
            // These are the defaults
            color: '#556b2f',
            backgroundColor: 'white'
        }, options);
}(jQuery));

$('a').greenify({
    color: 'orange'
}).showLinkLocation();

Basically all this does is change the text color and background-color with the provided element. Now what I am trying to do is convert this simple plugin into TypeScript

I have tried a few things and the closest I got is this.

TypeScript

/// <reference path="../../typings/jquery/jquery.d.ts" />

module Coloring
{
    interface IGreenifyOptions
    {
        color: string;
        backgroundColor: string;
    }

    export class GreenifyOptions implements IGreenifyOptions
    {
        // Fields
        color: string;
        backgroundColor: string;

        constructor(color: string, backgroundColor: string)
        {
            this.color = color;
            this.backgroundColor = backgroundColor;
        }
    }

    export class Greenify
    {
        // Fields
        element: JQuery;
        options: GreenifyOptions;

        constructor(element: JQuery, options: GreenifyOptions)
        {
            this.element = element;
            this.options = options;

            this.OnCreate();
        }

        OnCreate()
        {
            this.element.css('color', this.options.color).css('background-color', this.options.backgroundColor);
        }
    }
}

JQuery which calls it

$(function ()
{
    var options: Coloring.GreenifyOptions = new Coloring.GreenifyOptions('#0F0', '#000');

    var $elems = $('a');
    $elems.each(function()
    {
        var result = new Coloring.Greenify($(this), options)
    });
});

However I don't want to provide the element like the above new Coloring.Greenify($(this), options) , I basically want to do something like this

$('a').Coloring.Greenify(options);

or

$('a').Coloring.Greenify(Coloring.GreenifyOptions()
{
    color: '#F0F',
    backgroundColor: '#FFF'
});

But I can't seem to figure how to tell TypeScript that the element it is attached to already is the Jquery element. Could anyone shine some light on this to help me out.

PS the above I have works fine, I just want to change the calling code.


Update

This is what I have at the moment and it works

TypeScript interface JQuery { Greenify(); Greenify(options: Coloring.GreenifyOptions); }

(function ($)
{
    $.fn.Greenify = function (options)
    {
        return new Coloring.Greenify(this, options);
    }
})(jQuery);

jQuery var $elems = $('a').Greenify(options);

However it means I have to provide options and if I do the constructor without options I get options is undefined. The answer I have ticked as correct is correct for the question I have asked. However just keep in mind that the answer provided required you provide options into your typescript constructor, I am going to see on how to have default options and then override them in the constructor but this is a different question :)


Update 2

Just to let everyone know I have found a way to provide options to your plugin or not.

What you can do is this

TypeScript class

export class Greenify
    {
        // Default Options
        static defaultOptions: IGreenifyOptions =
        {
            color: '#F00',
            backgroundColor: '#00F'
        };

        // Fields
        element: JQuery;
        options: GreenifyOptions;

        constructor(element: JQuery, options: GreenifyOptions)
        {
            // Merge options
            var mergedOptions: GreenifyOptions = $.extend(Greenify.defaultOptions, options);
            this.options = mergedOptions;
            this.element = element;

            this.OnCreate();
        }

        OnCreate()
        {
            this.element.css('color', this.options.color).css('background-color', this.options.backgroundColor);
        }

    }

TypeScript Interface

interface JQuery
{
    Greenofy();
    Greenify(obj?: any);
    Greenify(options?: Coloring.GreenifyOptions);
}

(function ($)
{
    $.fn.Greenify = function (options)
    {
        return new Coloring.Greenify(this, options);
    }
})(jQuery);

jQuery code to call the plugin with one optional option

$(function ()
{
    var $elems = $('a').Greenify(<Coloring.GreenifyOptions>{
        color: '#00F'
    });
});

So not the output will make the anchors background color '#00F' which is the default and the option I have provided will make the anchor text color '#00F'.

I hope this helps anyone else that is having the same problem as me :)

Creating jQuery plugins along with TypeScript can get a bit messy. I personally prefer keeping the jQuery plugin chaining syntax, mostly for consistency and maintainability .

So, after the module declaration you can basically wrap around your implementation extending the jQuery prototype as:

module Coloring {
  interface IGreenifyOptions {
    color: string;
    backgroundColor: string;
  }

  export class GreenifyOptions implements IGreenifyOptions {
    // Fields
    color: string;
    backgroundColor: string;

    constructor(color: string, backgroundColor: string) {
      this.color = color;
      this.backgroundColor = backgroundColor;
    }
  }

  export class Greenify {
    // Fields
    element: JQuery;
    options: GreenifyOptions;

    constructor(element: JQuery, options: GreenifyOptions) {
      this.element = element;
      this.options = options;

      this.OnCreate();
    }

    OnCreate() {
      this.element.css('color', this.options.color).css('background-color', this.options.backgroundColor);
    }
  }
}


//jquery plugin wrapper.
;
(function(w, $) {
    //no jQuery around
  if (!$) return false;

  $.fn.extend({
    Coloring: function(opts) {
      //defaults
      var defaults: Coloring.GreenifyOptions = new Coloring.GreenifyOptions('#0F0', '#000');

      //extend the defaults!
      var opts = $.extend({}, defaults, opts)

      return this.each(function() {
        var o = opts;
        var obj = $(this);
        new Coloring.Greenify(obj, o);

      });
    }
  });
})(window, jQuery);

Fetching the plugin as:

$(function() {

  var $a = $('a').Coloring();
  var $div = $('div').Coloring({
    color: '#F0F',
    backgroundColor: '#FFF'
  });
  var $div = $('strong').Coloring({
    color: '#gold',
    backgroundColor: 'pink'
  });
});

Demo

You can create and reference your own definitions file greenify.d.ts and add the function like this:

interface Jquery {
     greenify: (options: Coloring.IGreenifyOptions) => void
}

Then you can simple call it like:

$('a').greenify(new GreenifyOptions(...));

or

$('a').greenify({color:'', backgroundColor:''});

Explanation:

At compile time typescript will actually merge all interface definitions.

Side Note:

if you're adamant about having it exactly as $('a').Coloring.Greenify(..) then you'll have to change a lot more:

  • Coloring couldnt be your module's name anymore as you'd have to use it in the interface definition above
  • You would probably have to create a class that has .Greenify as static method
  • Possibly more...

All in all it's probably easier to stick with my initial solution as it its a bit less verbose but that's up to you.

Hope that helps

Update:

To account for the default options you can modify the interface definition to have overrides:

interface Jquery {
     greenify: () => void;
     greenify: (options: Coloring.IGreenifyOptions) => void;
}

interface IGreenifyOptions
{
    color?: string;
    backgroundColor?: string;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM