Definiera en anpassad jQuery UI-widget i TypeScript


Vi tittar för närvarande på att översätta vårt JavaScript-projekt till TypeScript. Vår applikation är starkt beroende av egenutvecklade jQuery UI-widgetar.

I vår nuvarande kodbas använder vi en mekanism för djup kopiering för att ärva från widgetdefinitioner, vilket tillåter oss, till exempel, att deklarera en generell TableWidget samt en OrdersTableWidget som definierar mer specifika funktioner.

Därför skulle jag vilja definiera mina widgetdefinitioner som TypeScript-klasser och sedan binda en instans av dessa klasser till jQuery.

Till exempel

class MyWidget {
    options: WidgetOptions;
    _init(){
        // general initialization
    }
}

class MySecondWidget extends MyWidget {
    _init(){
        super._init();
        // specific initialization
    }
}

Och sedan

$.widget("MyNameSpace.MyWidget", new MyWidget());
$.widget("MyNameSpace.MySeWidget", new MyWidget());

Dessutom skulle jag vilja ange mina anpassade widgets som implementeringar av jQuery UIs Widget-definition

class MyWidget implements Widget {
    options: WidgetOptions;
    _init(){
        // general initialization
    }
}

så att jag kan använda följande syntax i TypeScript:

$(selector).MyWidget(options);

Jag vet att jag måste arbeta med definitionsfilen (från DefinitelyTyped), men jag har ännu inte hittat en pålitlig källa som förklarar hur jag ska skriva egna jQuery UI-widgets i TypeScript. Har någon erfarenhet av detta?

All hjälp uppskattas mycket, som alltid!

Svar #1

Jag är inte säker på att du kan skriva en klass som implementerar Widget-gränssnittet, på grund av bristen på överlagrade konstruktörer. Du kan skapa en variabel som är typad av Widget-gränssnittet.

Ett standard jQuery-plugin skulle representeras i nästan ren JavaScript och skulle inte använda moduler eller klasser eftersom det i slutändan blir inbäddat som en del av jQuery, som i sig inte är en modul eller klass.

Här är ett tomt plugin som kallas plugin som ser ut som vilket standard jQuery-plugin som helst, men du kan se att det utnyttjar TypeScript-typ systemet och utvidgar JQuery-gränssnittet för att tillåta att det kan anropas.

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

interface JQuery {
    plugin(): JQuery;
    plugin(settings: Object): JQuery;
}

(function ($) {

    function DoSomething(someParamater: string) : void {

    }

    $.fn.plugin = function (settings) {

        var config = {
            settingA: "Example",
            settingB: 5
        };

        if (settings) {
            $.extend(config, settings);
        }

        return this.each(function () {

        });
    };

})(jQuery);

Detta skulle kallas på det normala sättet.

$('#id').plugin();

Så egentligen är mitt svar - du kan egentligen inte göra vad du vill eftersom du lägger till i de deklarerade gränssnitten för jQuery snarare än att exponera dem som moduler. Du skulle kunna kapsla in användningen i en modul, som en adapter som abstraherar bort jQuery-aspekten från användningen i din TypeScript, eller så kan du anropa dina klasser inifrån pluginen, men pluginen eller widgeten passar egentligen inte in i en modul eller klass.

Svar #2

Det kan vara till hjälp att ha en basklass i TypeScript från vilken andra widgetklasser kan härledas. Dess enda syfte är att tillhandahålla basklassens semantik så att du kan komma åt basklassens medlemmar utan att behöva använda svag typning.

Tricket är att ta bort alla medlemmar vid körning (i konstruktorn) -- annars stöter man på problem med den ärvning som tillhandahålls av widget-fabriken. Till exempel skulle option-metoden skriva över widgetens ursprungliga metod vilket inte är önskvärt: vi vill bara kunna kalla på den (på ett statiskt typat sätt).

class WidgetBase {

    public element:JQuery;

    constructor() {
        // remove all members, they are only needed at compile time.
        var myPrototype =  (<Function>WidgetBase).prototype;
        $.each(myPrototype, (propertyName, value)=>{
           delete myPrototype[propertyName];
        });
    }

    /**
     * Calles the base implementation of a method when called from a derived method.
     * @private
     */
    public _super(arg1?:any, arg2?:any, arg3?:any, arg4?:any) {

    }

    /**
     * @private
     */
    public _superApply(arguments) {

    }

    /**
     * Gets or sets the value of the widget option associated with the specified optionName.
     */
    public option(optionName:string, value?:any):any {

    }

    // ... further methods from http://api.jqueryui.com/jQuery.widget/
}

Då kan du implementera din egen widget på det här sättet:

class SmartWidget extends WidgetBase {

    constructor(){
        super();
    }

    public _create() {
        var mySmartOption = this.option('smart'); // compiles because of base class
        this.beSmart(mySmartOption);
    }

    public _setOption(key:string, value:any) {
        if (key === 'smart') {
           this.beSmart(value);
        }

        this._super(key, value); // compiles because of base class
    }

    private beSmart(smartOne:any){
        // ...
    }

}

// register
jQuery.widget("myLib.smartWidget", new SmartWidget());

// assuming you are using https://github.com/borisyankov/DefinitelyTyped
declare interface JQuery{
    smartWidget();
    smartWidget(options:any);
    smartWidget(methodName:string, param1?:any, param2?:any, param3?:any, param4?:any);
}

Och slutligen kan du använda din widget:

$(".selector").smartWidget({smart:"you"});

Relaterade artiklar