Använda jQuery-användarens widgetfabrik

Under lång tid var det enda sättet att skriva anpassade kontroller i jQuery att förlänga $ .fn namnutrymmet. Det här fungerar bra för enkla widgets, men när du börjar bygga mer stateful widgets blir det snabbt besvärligt. För att hjälpa till med att bygga widgets introducerade jQuery UI-teamet Widget Factory, vilket avlägsnar det mesta av kedjan som vanligtvis är förknippad med att hantera en widget.

Widgetfabriken, en del av jQuery UI Core, ger ett objektorienterat sätt att hantera livscykeln för en widget. Dessa livscykelaktiviteter omfattar:

  • Skapa och förstöra en widget
  • Ändra widgetalternativ
  • Göra "super"samtal i underklassade widgets
  • Händelseanmälningar

Låt oss utforska det här API-en, eftersom vi bygger en enkel punktdiagram-widget.


Bullet Chart Widget

Innan vi bygger den här widgeten, låt oss förstå några av byggblocken i widgeten. Bullet-diagrammet är ett koncept introducerat av Stephen Few som en variation på stapeldiagrammet.

Diagrammet består av en uppsättning barer och markörer som läggs på varandra för att indikera relativ prestanda. Det finns en kvantitativ skala för att visa det faktiska värdet. Genom att stapla staplarna och markörerna på så sätt kan mer information överföras utan att kompromissa med läsbarheten. Legenden berättar vilken typ av information vi planerar.

HTML för det här diagrammet ser ut så här:

 
Grön linje
0
25
50
75
100

Vår widget, som vi ringer till jquery.bulletchart, kommer dynamiskt att generera den här HTML-koden från de uppgifter som tillhandahålls. Den sista widgeten kan ses i källfilerna, som du kan ladda ner från GitHub. Samtalet att skapa widgeten ska se ut så här:

 $ 'chart'). bulletchart (storlek: 86, staplar: [title: 'Projicerat mål', värde: 75, css: ", title: 'Actual Target', värde: 50, css: ' blå "], markörer: [titel:" Grön linje ", värde: 80, css:" grön ", titel:" Minsta tröskelvärde ", värde: 50, css:" rött "] 0, 25, 50, 75, 100]);

Alla värden är i procent. De storlek Alternativet kan användas när du vill ha flera punktrapporter placerade bredvid varandra med relativ storlek. De fästingar alternativet används för att placera etiketterna på skalan. Markörerna och staplarna specificeras som en grupp av objektstavlar med titel, värde och css egenskaper.


Bygga Widget

Nu när vi känner till widgetens struktur, låt oss gå ner för att bygga den. En widget skapas genom att ringa $ .Widget () med namnet på widgeten och ett objekt som innehåller dess instansmetoder. Det exakta APIet ser ut som:

jQuery.widget (namn [, bas], prototyp)

För nu ska vi arbeta med bara namnet och prototypen. För bulletchart ser vår grundläggande widgetstub ut som följande:

 $ .widget ('nt.bulletchart', options: , _create: function () , _destroy: funktion () , _setOption: funktion (nyckel, värde) );

Det rekommenderas att du alltid namespace dina widget namn. I det här fallet använder vi "nt.bulletchart'. Alla jQuery UI-widgets är under "uinamnrymd. Även om vi namnger widgeten, innehåller samtalet om att skapa en widget på ett element inte namnrymden. Således skulle vi bara ringa för att skapa ett kulschema $ (# Elem). Bulletchart ().

Instansegenskaperna anges efter widgetens namn. Enligt konventionen bör alla privata metoder i widgeten prefixas med '_'. Det finns några speciella egenskaper som förväntas av widgetfabriken. Dessa inkluderar alternativ, _skapa, _förstöra och _setOption.

  • alternativ: Det här är standardalternativen för widgeten
  • _skapa: Widgetfabriken kallar den här metoden första gången widgeten är instanserad. Detta används för att skapa den ursprungliga DOM och bifoga händelsehanterare.
  • _i det: Efter samtalet till _skapa, fabriken ringer _i det. Detta används i allmänhet för att återställa widgeten till initialt tillstånd. När en widget är skapad kallar du den enkla widgetkonstruktören, t.ex. $ .Bulletchart (), kommer också att återställa widgeten. Detta ringer internt _i det.
  • _setOption: Kallas när du ställer in ett alternativ i widgeten, med ett samtal som: $ ('# elem'). bulletchart ('alternativ', 'storlek', 100). Senare ser vi andra sätt att ställa in alternativ på widgeten.

Skapa den ursprungliga DOM med _skapa

Vår widget för bulletchart kommer till liv i _skapa metod. Här bygger vi grundstrukturen för diagrammet. De _skapa funktionen kan ses nedan. Du märker att det inte händer mycket här förutom att skapa den högsta behållaren. Det faktiska arbetet med att skapa DOM för barer, markörer och fästingar händer i _setOption metod. Det kan tyckas vara något kontraintetivt att börja med, men det finns en giltig anledning till det.

 _create: function () this.element.addClass ('bullet-chart'); // kartongbehållare this._container = $ ('
') .appendTo (this.element); this._setOptions ('size': this.options.size, 'ticks': this.options.ticks, 'barer': this.options.bars, 'markers': this.options.markers);

Observera att staplarna, markörerna och fästningarna också kan ändras genom att ställa in alternativ på widgeten. Om vi ​​behöll koden för sin konstruktion inuti _skapa, vi skulle upprepa oss inomhus _setOption. Genom att flytta koden till _setOption och åberopa det från _skapa tar bort dubbelarbete och centraliserar också konstruktionen.

Dessutom visar koden ovan ett annat sätt att ställa in alternativ på widgeten. Med _setOptions metod (notera flertalet), kan du ställa in multipelalternativ på en gång. Internt gör fabriken enskilda samtal _setOption för var och en av alternativen.

De _setOption metod

För kullplanet, _setOption Metod är arbetshästen. Den hanterar skapandet av markörer, barer och fästingar samt eventuella ändringar som görs på dessa egenskaper. Det fungerar genom att rensa alla befintliga element och återskapa dem baserat på det nya värdet.

De _setOption Metoden tar emot både nyckeln och ett värde som argument. Nyckeln är namnet på alternativet, vilket ska motsvara en av nycklarna i standardalternativen. Om du till exempel vill ändra raderna på widgeten skulle du göra följande samtal:

$ ('# elem'). bulletchart ('option', 'bars', [titel: 'Ny markör', värde: 50])

De _setOption Metoden för bulletchart ser ut så här:

 _setOption: funktion (nyckel, värde) var self = this, prev = this.options [key], fnMap = 'bars': funktion () createBars (värde, själv); , "markörer": funktion () createMarkers (värde, själv); , "ticks": funktion () createTickBar (värde, jag); , "storlek": funktion () self.element.find ('chart-container') .css ('bredd', värde + '%'); ; // basera detta._super (nyckel, värde); om (nyckel in fnMap) fnMap [key] (); // Brand händelse this._triggerOptionChanged (nyckel, tidigare, värde); 

Här skapar vi en enkel hash av alternativnamnet till motsvarande funktion. Med hjälp av denna hash arbetar vi bara med giltiga alternativ och tyst ignorerar ogiltiga. Det här händer två saker: ett samtal till _super() och skjuta alternativet ändrat händelse. Vi kommer att titta på dem senare i den här artikeln.

För varje av de alternativ som ändrar DOM, kallar vi en specifik hjälparätt. Hjälpen metoder, createBars, createMarkers och createTickBar specificeras utanför widgetens instansegenskaper. Detta beror på att de är desamma för alla widgets och behöver inte skapas individuellt för varje widget-instans.

// Creation funktioner funktion createTickBar (fästingar, widget) // Rensa befintlig widget._container.find ('. Tick-bar'). Ta bort (); var tickBar = $ ('
'); $ .each (fästingar, funktion (idx, tick) var t = $ ('
') .css (' left ', tick +'% '); var tl = $ ('
') .css (' left ', tick +'% ') .text (tick); tickBar.append (t); tickBar.append (tl); ); widget._container.append (tickBar); funktion createMarkers (markörer, widget) // Rensa befintlig widget._container.find ('.markör'). ta bort (); $ .each (markörer, funktion (idx, m) varemarkör = $ ('
') .css (vänster: m.value +'% ') .addClass (m.css) .attr (' markör-index ', idx); widget._container.append (markör); ); function createBars (barer, widget) // Rensa befintlig widget._container.find ('. bar'). ta bort (); $ .each (staplar, funktion (idx, stapel) var bar = $ ('
') .css (vänster: 0, bredd:' 0% ') .addClass (bar.css) .attr (' bar-index ', idx) .animate (width: bar.value +'% ' ); widget._container.append (bar); );

Alla skapningsfunktionerna arbetar med procentandelar. Detta säkerställer att diagrammet reflows snyggt när du ändrar det innehållande elementet.

Standardalternativen

Utan några alternativ som anges när widgeten skapas kommer standardinställningarna att spelas in. Detta är rollen för alternativ fast egendom. För bulletchart ser våra standardalternativ ut så här:

 $ .widget ('nt.bulletchart', alternativ: // procent: 0-100 storlek: 100, // [titel: 'Provfält', värde: 75, css: "], staplar: [] , // [title: "Provmarkör", värde: 50, css: "], markörer: [], // fästingar - procentvärden fästingar: [0, 10, 20, 30, 40, 50, 60 , 70, 80, 90, 100], ...

Vi börjar med en storlek på 100%, inga barer och markörer och med fästingar placerade varje 10%. Med dessa standardvärden ska vårt kollaboratorium se ut som:

Hittills har vi sett hur man skapar widgeten med _skapa och uppdatera den med _setOption. Det finns en annan livscykelmetod som kommer att kallas när du förstör en widget. Det här är _förstöra metod. När du ringer $ (# Elem). Bulletchart (förstöra), widgetfabriken ringer internt _förstöra på din widget instans. Widgeten ansvarar för att ta bort allt som det introducerades i DOM. Detta kan innehålla klasser och andra DOM-element som lagts till i _skapa metod. Detta är också ett bra ställe att koppla bort alla händelsehanterare. De _förstöra bör vara exakt motsatsen till _skapa metod.

För kollapsdiagram widgeten, den _förstöra är ganska enkelt:

 _destroy: funktion () this.element.removeClass ('bullet-chart'); this.element.empty (); ,

Underklasser, Evenemang och mer

Vår widget för bulletchart är nästan komplett, förutom en sista funktion: legend. Legenden är helt nödvändig, eftersom det kommer att ge mer mening åt markörerna och staplarna. I det här avsnittet lägger vi till en legend bredvid diagrammet.

I stället för att lägga till den här funktionen direkt till bulletchart-widgeten skapar vi en underklass, bulletchart2, som kommer att ha legenden stöd. I processen kommer vi också att titta på några av de intressanta egenskaperna hos Widget Factory arv.

Lägga till en legend

Widget Factory stöder underklassificering av en widget för att skapa mer specialiserade versioner. Tidigare i artikeln såg vi API för $ .Widget (), som hade tre argument:

jQuery.widget (namn [, bas], prototyp)

Den andra parametern tillåter oss att välja en basklass för vår widget. Vår bulletchart2 widget, vilka underklasser bulletchart, kommer att ha följande signatur:

 $ .widget ('nt.bulletchart2', $ .nt.bulletchart, options: // Visa / dölj legend legend: true, // det här säkerställer att vi behåller samma namnrymd som bas widgetEventPrefix: $ .nt.bulletchart .prototype.widgetEventPrefix, _create: function () ..., _destroy: funktion () ..., _setOption: funktion (nyckel, värde) ...)

Det finns några intressanta saker att notera här:

  • Vi fortsätter att namnrymd vårt widgetnamn: nt.bulletchart2.
  • Widgetfabriken lägger automatiskt widgeten under $ .nt namnutrymmet. Således, för att referera till vår tidigare widget, använde vi $ .nt.bulletchart. På samma sätt om vi skulle subklassera en av standard jQuery UI-widgets, skulle vi referera dem till $ .Ui.widget namn
  • De widgetEventPrefix är en ny egendom som vi inte har sett tidigare. Vi kommer att få det när vi pratar om händelser. Resten av instansegenskaperna borde vara bekanta.

Eftersom vi lägger till fler DOM-element med legenden måste vi åsidosätta _skapa metod. Detta innebär också att vi måste överväga _förstöra, för att vara symmetrisk.

 _create: function () var self = this; this._legend = $ ('
') .appendTo (this.element); ... // Ring basen this._super (); this._setOption ('legend', this.options.legend); , _destroy: funktion () this.element.find ('legend'). empty (); ... this._super (); ,

Här ser vi igen samma mönster som vår tidigare _skapa metod. Vi skapar behållaren för legenden och ringer sedan _setOption att bygga resten av legenden. Eftersom vi överstyrer _skapa, Vi måste se till att vi kallar basen _skapa. Vi gör detta med samtalet till _super. På samma sätt, i _förstöra, vi ser också samtalet till _super.

Nu kanske du undrar: hur vet widgeten vilken supermetod som ska ringas med en enkel okvalificerad _super åkallan? Smarts för det ligger i tarmarna i widgetfabriken. När en widget är underklassad ställer fabriken in _super referens annorlunda för var och en av instansfunktionerna. Således, när du ringer _super från din instansmetod pekar det alltid på rätt sätt _super metod.

Eventmeddelanden

Eftersom bulletchart stöder byta markörer och staplar måste legenden synkronisera med de här ändringarna. Dessutom stöder vi också siktning av markörer och staplar genom att klicka på legendens objekt. Detta blir användbart när du har flera markörer och staplar. Genom att gömma några av elementen kan du se de andra tydligare.

För att stödja synkronisering av legenden med ändringarna till markörer och staplar, bulletchart2 widget måste lyssna på eventuella förändringar som händer med dessa egenskaper. Basen bulletchart bränder redan en förändrings händelse varje gång dess alternativ ändras. Här är motsvarande kod i basgränssnittet:

 _setOption: funktion (nyckel, värde) var själv = detta, före = this.options [key]; ... // base this._super (nyckel, värde); om (nyckel in fnMap) fnMap [key] (); // Brand händelse this._triggerOptionChanged (nyckel, tidigare, värde);  

När ett alternativ är inställt, setoption händelsen avfyras. Händelsedata innehåller föregående och nytt värde för det alternativ som ändrades.

Genom att lyssna på den här händelsen i den underklassade widgeten kan du veta när markörerna eller staplarna ändras. De bulletchart2 widgeten abonnerar på den här händelsen i dess _skapa metod. Prenumerera på widgets händelser uppnås med samtalet till this.element.on (). this.element pekar på elementet jQuery som widgeten var instanserad av. Eftersom evenemanget kommer att sparkas på elementet måste vår händelseabonnemang hända.

 _create: function () var self = this; this._legend = $ ('
') .appendTo (this.element); ... // Anmäl legenden om ändringar till markörer och staplar this.element.on (' bulletchart: setoption ', funktion (händelse, data) if (data.option ===' markörer ') createLegend (data.current, self.options.bars, self); annars om (data.option ===' barer ') createLegend (self.options.markers, data.current, self); ); // Ring basen this._super (); this._setOption ('legend', this.options.legend);

Notera händelsens namn som används för att prenumerera: 'Bulletchart: setoption'. Som en policy fäster widgetfabriken ett händelseprefix för händelser som avfyras från widgeten. Som standard är detta prefix namnet på widgeten, men det här kan enkelt ändras med widgetEventPrefix fast egendom. Basen bulletchart-widgeten ändrar detta till 'Bulletchart:'.

$ .widget ('nt.bulletchart', options: ..., widgetEventPrefix: 'bulletchart:' ...);

Vi måste också prenumerera på 'klick' händelser på legendens föremål för att dölja / visa motsvarande markör / stapel. Vi gör det här med _på metod. Denna metod tar en hash av händelse signaturen till handlarfunktionen. Handlarens sammanhang (detta) är korrekt inställt på widget-instansen. En annan bekvämlighet med _på är att widgetfabriken automatiskt binder händelserna till förstörelse automatiskt.

 _create: function () ... // Lyssna på klick på legend-objekten this._on ('click .legend-item': funktion (händelse) var elt = $ (event.currentTarget), item = elt.data ('diagram-objekt'), selector = '[' + item.type + '-index =' + item.index + ']'; this.element.find (selector) .fadeToggle (); elt.toggleClass blekna ");); ...

Fler tips

Widgetfabriken packar några andra trevliga saker som du borde vara medveten om.

Hänvisning till widget-instansen

Hittills har vi bara sett ett sätt att ringa metoder på widgeten. Vi gjorde det här med $ (# Elem) .bulletchart (metod namn). Detta tillåter dock endast offentliga metoder som "alternativ", "förstör", "på", "av". Om du vill använda dessa metoder direkt på widget-instansen finns det ett sätt att göra det. Widgetfabriken bifogar widget-instansen till data() objektets syfte. Du kan få denna instans som sådan:

var widget = $ ('# elem'). data ('bulletchart'); widget.destroy ();

Dessutom, om du vill få tag på alla bulletsart widgets på sidan, finns det också en väljare för det:

var allCharts = $ (': nt-bulletchart');

Några speciella metoder

Det finns några speciella metoder som du bör vara medveten om, som används mindre ofta: _getCreateEventData () och _getCreateOptions (). Den förstnämnda används för att bifoga händelsedata för "skapa" händelsen som avfyras efter avslutat samtal till _skapa.

_getCreateOptions är för att bifoga ytterligare standardalternativ för widgeten eller tvingande befintliga. Alternativen för användaruppsättning överstyrs av den här metoden, vilket i sin tur överväger standard-widgetalternativen.


Sammanfattning

Vi är klara! Om du vill utforska ytterligare, bör referenserna nedan tjäna dig ganska bra. Naturligtvis är den bästa källan till information alltid källkod, själv. Jag skulle uppmuntra att läsa jquery.ui.widget-källan på GitHub.

  • JQueryUI Widget Factory API
  • Bildspel på Widget Factory