Per molto tempo, l'unico modo per scrivere controlli personalizzati in jQuery era estendere il $ .fn
namespace. Funziona bene per i widget semplici, tuttavia, quando inizi a creare widget con più stati, diventa rapidamente ingombrante. Per facilitare il processo di creazione dei widget, il team dell'interfaccia utente di jQuery ha introdotto Widget Factory, che rimuove la maggior parte del boilerplate generalmente associato alla gestione di un widget.
La fabbrica di widget, parte del jQuery UI Core, fornisce un modo orientato agli oggetti per gestire il ciclo di vita di un widget. Queste attività del ciclo di vita includono:
Esploriamo questa API, mentre costruiamo un semplice widget del grafico a proiettili.
Prima di costruire questo widget, comprendiamo alcuni dei componenti del widget. Il Bullet Chart è un concetto introdotto da Stephen Few come variante del grafico a barre.
Il grafico consiste in un insieme di barre e marker sovrapposti l'uno sull'altro per indicare la performance relativa. Esiste una scala quantitativa per mostrare l'effettivo intervallo di valori. Impilando le barre e i contrassegni in questo modo, è possibile trasmettere più informazioni senza comprometterne la leggibilità. La leggenda dice il tipo di informazioni che stiamo tracciando.
L'HTML per questo grafico è simile al seguente:
Linea verde0255075100
Il nostro widget, che chiameremo jquery.bulletchart
, genererà dinamicamente questo codice HTML dai dati forniti. Il widget finale può essere visualizzato nei file di origine, che è possibile scaricare da GitHub. La chiamata per creare il widget dovrebbe apparire così:
$ ('. chart'). bulletchart (size: 86, bars: [title: 'Projected Target', valore: 75, css: ", title: 'Target effettivo', valore: 50, css: ' blu '], marcatori: [title:' Green Line ', valore: 80, css:' green ', title:' Minimum Threshold ', valore: 50, css:' red '], tick: [ 0, 25, 50, 75, 100]);
Tutti i valori sono in percentuale. Il taglia
l'opzione può essere utilizzata quando si desidera disporre di più grafici puntati uno accanto all'altro con relativo dimensionamento. Il zecche
l'opzione è usata per mettere le etichette sulla scala. I marcatori e le barre sono specificati come una matrice di valori letterali oggetto con titolo
, valore
e css
proprietà.
Ora che conosciamo la struttura del widget, passiamo a costruirlo. Un widget viene creato chiamando $ .Widget ()
con il nome del widget e un oggetto contenente i suoi metodi di istanza. L'API esatta assomiglia a:
jQuery.widget (nome [, base], prototipo)
Per ora, lavoreremo solo con il nome e gli argomenti del prototipo. Per il diagramma di proiettile, il nostro stub di widget di base ha il seguente aspetto:
$ .widget ('nt.bulletchart', opzioni: , _create: function () , _destroy: function () , _setOption: function (chiave, valore) );
Si consiglia di assegnare sempre un nome ai nomi dei widget. In questo caso, stiamo usando 'nt.bulletchart'. Tutti i widget dell'interfaccia utente jQuery sono sotto 'ui'spazio dei nomi. Sebbene si stia assegnando il nome al widget, la chiamata per creare un widget su un elemento non include lo spazio dei nomi. Quindi, per creare un grafico a proiettili, chiameremmo semplicemente $ ( '# Elem'). Bulletchart ()
.
Le proprietà dell'istanza vengono specificate in base al nome del widget. Per convenzione, tutti i metodi privati del widget devono essere preceduti da "_". Ci sono alcune proprietà speciali che sono previste dalla fabbrica di widget. Questi includono il opzioni
, _creare
, _distruggere
e _setOption
.
opzioni
: Queste sono le opzioni predefinite per il widget_creare
: La fabbrica di widget chiama questo metodo la prima volta che il widget viene istanziato. Questo è usato per creare il DOM iniziale e allegare qualsiasi gestore di eventi._dentro
: Seguito alla chiamata a _creare
, la fabbrica chiama _dentro
. Questo è generalmente utilizzato per ripristinare il widget allo stato iniziale. Una volta creato un widget, chiamando il costruttore di widget semplice, ad esempio: $ .Bulletchart (), ripristinerà anche il widget. Questo internamente chiama _dentro
._setOption
: Chiamato quando si imposta un'opzione sul widget, con una chiamata come: $ ('# elem'). bulletchart ('option', 'size', 100)
. Più avanti vedremo altri modi per impostare le opzioni sul widget._creare
Il nostro widget bulletchart prende vita in _creare
metodo. Qui è dove costruiamo la struttura di base per il grafico. Il _creare
la funzione può essere vista sotto. Noterai che qui non sta accadendo molto oltre alla creazione del contenitore di livello superiore. Il vero lavoro di creare il DOM per barre, marker e tick avviene nel _setOption
metodo. Questo può sembrare un po 'contro-intuitivo per cominciare, ma c'è una ragione valida per quello.
_create: function () this.element.addClass ('bullet-chart'); // contenitore grafico this._container = $ ('') .appendTo (this.element); this._setOptions ('size': this.options.size, 'ticks': this.options.ticks, 'bars': this.options.bars, 'markers': this.options.markers);
Nota che le barre, i contrassegni e le zecche possono anche essere modificati impostando le opzioni sul widget. Se tenevamo il codice per la sua costruzione all'interno _creare
, ci ripeteremo dentro _setOption
. Spostando il codice in _setOption
e invocandolo da _creare
rimuove la duplicazione e centralizza anche la costruzione.
Inoltre, il codice sopra mostra un altro modo di impostare le opzioni sul widget. Con il _setOptions
metodo (si noti il plurale), è possibile impostare le opzioni mutiple in un colpo solo. Internamente, la fabbrica effettuerà chiamate individuali _setOption
per ciascuna delle opzioni.
_setOption
metodoPer il grafico proiettile, il _setOption
il metodo è il cavallo di battaglia. Gestisce la creazione di marcatori, barre e zecche e anche eventuali modifiche apportate a queste proprietà. Funziona cancellando tutti gli elementi esistenti e ricreandoli in base al nuovo valore.
Il _setOption
il metodo riceve sia la chiave di opzione che un valore come argomenti. La chiave è il nome dell'opzione, che dovrebbe corrispondere a uno dei tasti nelle opzioni predefinite. Ad esempio, per modificare le barre sul widget, si effettuerà la seguente chiamata:
$ ('# elem'). bulletchart ('option', 'bars', [title: 'New Marker', valore: 50]))
Il _setOption
il metodo per il diagramma di progetto è simile al seguente:
_setOption: function (chiave, valore) var self = this, prev = this.options [chiave], fnMap = 'bars': function () createBars (value, self); , 'marcatori': function () createMarkers (valore, self); , 'ticks': function () createTickBar (valore, self); , 'size': function () self.element.find ('. chart-container') .css ('width', value + '%'); ; // base this._super (chiave, valore); if (digita fnMap) fnMap [key] (); // Evento Fire this._triggerOptionChanged (chiave, anteprima, valore);
Qui, creiamo un semplice hash del nome-opzione per la funzione corrispondente. Usando questo hash, lavoriamo solo su opzioni valide e ignoriamo silenziosamente quelli non validi. Ci sono altre due cose che accadono qui: una chiamata a _super()
e sparando l'opzione ha cambiato evento. Li esamineremo più avanti in questo articolo.
Per ciascuna delle opzioni che cambia il DOM, chiamiamo un metodo di supporto specifico. I metodi di supporto, createBars
, createMarkers
e createTickBar
sono specificati al di fuori delle proprietà dell'istanza del widget. Questo perché sono gli stessi per tutti i widget e non è necessario che siano creati individualmente per ogni istanza del widget.
// Funzione funzioni di creazione createTickBar (tick, widget) // Cancella widget._container.find esistente ('. Tick-bar'). Remove (); var tickBar = $ (''); $ .each (tick, funzione (idx, tick) var t = $ ('') .css (' left ', tick +'% '); var tl = $ ('') .css (' left ', tick +'% ') .text (tick); tickBar.append (t); tickBar.append (tl); ); widget._container.append (tickBar); function createMarkers (marker, widget) // Cancella widget._container.find esistente ('. marker'). remove (); $ .each (marcatori, funzione (idx, m) var marker = $ ('') .css (left: m.value +'% ') .addClass (m.css) .attr (' marker-index ', idx); widget._container.append (marker); ); function createBars (bars, widget) // Cancella widget._container.find esistente ('. bar'). remove (); $ .each (barre, funzione (idx, barra) var bar = $ ('') .css (left: 0, width:' 0% ') .addClass (bar.css) .attr (' bar-index ', idx) .animate (width: bar.value +'% ' ); widget._container.append (bar); );
Tutte le funzioni di creazione operano su percentuali. Ciò garantisce che il grafico si rifletta bene quando ridimensioni l'elemento contenitore.
Senza le opzioni specificate durante la creazione del widget, entrano in gioco le impostazioni predefinite. Questo è il ruolo del opzioni
proprietà. Per il diagramma di proiettile, le nostre opzioni di default sono così:
$ .widget ('nt.bulletchart', options: // percentage: 0 - 100 size: 100, // [title: 'Sample Bar', valore: 75, css: "], barre: [] , // [title: 'Sample Marker', valore: 50, css: "], marker: [], // ticks - valori percentuali tick: [0, 10, 20, 30, 40, 50, 60 , 70, 80, 90, 100], ...
Iniziamo con una dimensione di 100%, niente barre e marcatori e con tick piazzati ogni 10%. Con questi valori predefiniti, il nostro grafico a proiettili dovrebbe essere simile a:
Finora, abbiamo visto come creare il widget usando _creare
e aggiornandolo usando _setOption
. Esiste un altro metodo del ciclo di vita, che verrà chiamato quando si distrugge un widget. Questo è il _distruggere
metodo. Quando chiami $ ( '# Elem'). Bulletchart ( 'distruggere')
, la fabbrica di widget chiama internamente _distruggere
sull'istanza del widget. Il widget è responsabile della rimozione di tutto ciò che ha introdotto nel DOM. Questo può includere classi e altri elementi DOM aggiunti in _creare
metodo. Questo è anche un buon posto per dissipare qualsiasi gestore di eventi. Il _distruggere
dovrebbe essere l'esatto opposto del _creare
metodo.
Per il widget del grafico a proiettile, il _distruggere
è abbastanza semplice:
_destroy: function () this.element.removeClass ('bullet-chart'); this.element.empty (); ,
Il nostro widget bulletchart è quasi completo, tranne un'ultima funzione: leggenda. La leggenda è abbastanza essenziale, poiché darà più significato ai marcatori e alle barre. In questa sezione aggiungeremo una legenda accanto al grafico.
Piuttosto che aggiungere questa funzione direttamente al widget del diagramma di proiettile, creeremo una sottoclasse, bulletchart2
, che avrà il supporto della leggenda. Nel processo, esamineremo anche alcune delle caratteristiche interessanti dell'ereditarietà di Widget Factory.
The Widget Factory supporta la sottoclasse di un widget per creare versioni più specializzate. All'inizio dell'articolo, abbiamo visto l'API per $ .Widget ()
, che aveva tre argomenti:
jQuery.widget (nome [, base], prototipo)
Il secondo parametro ci consente di scegliere una classe base per il nostro widget. Nostro bulletchart2
widget, che sottoclasse bulletchart
, avrà la seguente firma:
$ .widget ('nt.bulletchart2', $ .nt.bulletchart, options: // Mostra / nascondi legenda della legenda: true, // questo ci assicura di mantenere lo stesso spazio dei nomi del widget baseEventPrefix: $ .nt.bulletchart .prototype.widgetEventPrefix, _create: function () ..., _destroy: function () ..., _setOption: function (chiave, valore) ...)
Ci sono alcune cose interessanti da notare qui:
nt.bulletchart2
.$ .nt.bulletchart
. Allo stesso modo, se dovessimo creare una sottoclasse di uno dei widget standard dell'interfaccia utente di jQuery, faremo riferimento a loro $ .Ui.widget-nome
widgetEventPrefix
è una nuova proprietà che non abbiamo mai visto prima. Ci arriveremo quando parliamo di eventi. Il resto delle proprietà dell'istanza dovrebbe essere familiare.Dato che stiamo aggiungendo altri elementi DOM con la legenda, dovremo ignorare il _creare
metodo. Ciò significa anche che dobbiamo scavalcare _distruggere
, per essere simmetrici.
_create: function () var self = this; this._legend = $ ('') .appendTo (this.element); ... // Chiama la base this._super (); this._setOption ('legend', this.options.legend); , _destroy: function () this.element.find ('. legend'). empty (); ... this._super (); ,
Qui, di nuovo, vediamo lo stesso schema del nostro precedente _creare
metodo. Creiamo il contenitore per la legenda e poi chiamiamo _setOption
per costruire il resto della leggenda. Dal momento che stiamo ignorando il _creare
, dobbiamo assicurarci di chiamare la base _creare
. Lo facciamo con la chiamata a _super
. Allo stesso modo, in _distruggere
, vediamo anche la chiamata a _super
.
Ora ti starai chiedendo: come fa il widget a sapere quale super-metodo chiamare con un semplice non qualificato _super
invocazione? L'intelligenza per quello si trova nelle viscere della fabbrica di widget. Quando un widget è sottoclasse, la fabbrica imposta il _super
riferimento diverso per ciascuna delle funzioni di istanza. Quindi, quando chiami _super
dal tuo metodo di istanza, punta sempre al corretto _super
metodo.
Poiché il diagramma di proiettili supporta la modifica di indicatori e barre, la legenda deve essere sincronizzata con tali modifiche. Inoltre, supporterà anche la commutazione della visibilità di marcatori e barre facendo clic sugli elementi della legenda. Questo diventa utile quando hai diversi indicatori e barre. Nascondendo alcuni elementi, puoi vedere gli altri più chiaramente.
Per supportare la sincronizzazione della legenda con le modifiche ai marcatori e alle barre, il file bulletchart2
il widget deve ascoltare i cambiamenti che si verificano su tali proprietà. Il bulletchart di base genera già un evento di modifica ogni volta che le sue opzioni cambiano. Ecco lo snippet corrispondente dal widget di base:
_setOption: function (chiave, valore) var self = this, prev = this.options [chiave]; ... // base this._super (chiave, valore); if (digita fnMap) fnMap [key] (); // Evento Fire this._triggerOptionChanged (chiave, anteprima, valore); , _triggerOptionChanged: function (optionKey, previousValue, currentValue) this._trigger ('setOption', type: 'setOption', opzione: optionKey, precedente: previousValue, current: currentValue);
Ogni volta che viene impostata un'opzione, il setOption
l'evento è licenziato. I dati dell'evento contengono il valore precedente e il nuovo dell'opzione che è stata modificata.
Ascoltando questo evento nel widget sottoclasse, puoi sapere quando cambiano i marker o le barre. Il bulletchart2
widget si iscrive a questo evento nel suo _creare
metodo. L'iscrizione agli eventi widget si ottiene con la chiamata a this.element.on ()
. this.element
punta all'elemento jQuery su cui è stato istanziato il widget. Dato che l'evento sarà attivato sull'elemento, il nostro abbonamento all'evento deve avvenire su questo.
_create: function () var self = this; this._legend = $ ('') .appendTo (this.element); ... // Applica la legenda alle modifiche ai marcatori e alle barre this.element.on (' bulletchart: setoption ', function (event, data) if (data.option ===' markers ') createLegend (data.current, self.options.bars, self); else if (data.option ===' bars ') createLegend (self.options.markers, data.current, self); ); // Chiama la base this._super (); this._setOption ('legend', this.options.legend);
Nota il nome dell'evento utilizzato per la sottoscrizione: 'Bulletchart: SetOption'
. Come criterio, il factory dei widget allega un prefisso evento per gli eventi attivati dal widget. Per impostazione predefinita, questo prefisso è il nome del widget, ma può essere facilmente modificato con widgetEventPrefix
proprietà. Il widget di base del diagramma di controllo lo modifica 'Bulletchart:'
.
$ .widget ('nt.bulletchart', options: ..., widgetEventPrefix: 'bulletchart:' ...);
Dobbiamo anche abbonarci a 'clic'
eventi sulle voci della legenda per nascondere / mostrare il corrispondente indicatore / barra. Lo facciamo con il _sopra
metodo. Questo metodo accetta un hash della firma dell'evento nella funzione del gestore. Il contesto del gestore (Questo
) è impostato correttamente sull'istanza del widget. Un'altra comodità con _sopra
è che la fabbrica di widget smantella automaticamente gli eventi su destroy.
_create: function () ... // Ascolta i clic sulla legenda-item this._on ('click .legend-item': function (event) var elt = $ (event.currentTarget), item = elt.data ('chart-item'), selector = '[' + item.type + '-index =' + item.index + ']'; this.element.find (selector) .fadeToggle (); elt.toggleClass (' dissolvenza ');); ...
La fabbrica di Widget racchiude alcune altre sottigliezze di cui dovresti essere a conoscenza.
Finora, abbiamo visto solo un modo di chiamare metodi sul widget. Abbiamo fatto questo con $ ( '# Elem) .bulletchart (' metodo-name ')
. Tuttavia, questo consente solo di chiamare metodi pubblici come "option", "destroy", "on", "off". Se si desidera richiamare tali metodi direttamente sull'istanza del widget, esiste un modo per farlo. La fabbrica di widget collega l'istanza del widget al dati()
oggetto dell'elemento. Puoi ottenere questa istanza in questo modo:
var widget = $ ('# elem'). data ('bulletchart'); widget.destroy ();
Inoltre, se si desidera ottenere una sospensione di tutti i widget di bulletchart sulla pagina, esiste anche un selettore per questo:
var allCharts = $ (': nt-bulletchart');
Ci sono alcuni metodi speciali di cui dovresti essere a conoscenza, che vengono usati meno frequentemente: _getCreateEventData ()
e _getCreateOptions ()
. Il primo viene utilizzato per allegare i dati dell'evento per l'evento "create" che viene generato dopo aver terminato la chiamata _creare
.
_getCreateOptions
è per allegare opzioni predefinite aggiuntive per il widget o per sovrascrivere quelle esistenti. Le opzioni fornite dall'utente sostituiscono le opzioni restituite da questo metodo, che a sua volta sostituisce le opzioni del widget predefinito.
Questo è un involucro! Se vuoi approfondire ulteriormente, i riferimenti qui sotto dovrebbero servire abbastanza bene. Ovviamente, la migliore fonte di informazione sarà sempre il codice sorgente, di per sé. Vorrei incoraggiare la lettura della sorgente jquery.ui.widget su GitHub.