Verwendung der Widget-Factory von jQuery
German (Deutsch) translation by Valentina (you can also view the original English article)
Lange Zeit bestand die einzige Möglichkeit, benutzerdefinierte Steuerelemente in jQuery zu schreiben, darin, den Namespace $.fn
zu erweitern. Dies funktioniert gut für einfache Widgets. Wenn Sie jedoch mit dem Erstellen von Stateful-Widgets beginnen, wird dies schnell umständlich. Um das Erstellen von Widgets zu erleichtern, hat das jQuery-UI-Team die Widget Factory eingeführt, mit der der größte Teil der Boilerplate entfernt wird, die normalerweise mit der Verwaltung eines Widgets verbunden ist.
Die Widget-Factory, die Teil des jQuery UI Core ist, bietet eine objektorientierte Möglichkeit, den Lebenszyklus eines Widgets zu verwalten. Diese Lebenszyklusaktivitäten umfassen:
- Erstellen und Zerstören eines Widgets
- Widget-Optionen ändern
- "Super"-Anrufe in Widgets mit Unterklassen tätigen
- Ereignisbenachrichtigungen
Lassen Sie uns diese API untersuchen, während wir ein einfaches Aufzählungsdiagramm-Widget erstellen.
Das Bullet Chart Widget
Bevor wir dieses Widget erstellen, wollen wir einige der Bausteine des Widgets verstehen. Das Bullet Chart ist ein Konzept, das von Stephen Few als Variation des Balkendiagramms eingeführt wurde.



Das Diagramm besteht aus einer Reihe von Balken und Markierungen, die übereinander liegen, um die relative Leistung anzuzeigen. Es gibt eine quantitative Skala, um den tatsächlichen Wertebereich anzuzeigen. Durch Stapeln der Balken und Markierungen auf diese Weise können mehr Informationen übermittelt werden, ohne die Lesbarkeit zu beeinträchtigen. Die Legende gibt Auskunft über die Art der Informationen, die wir zeichnen.
Der HTML-Code für dieses Diagramm sieht folgendermaßen aus:
<!-- Chart Container --> <div class="chart bullet-chart"> <!-- Legend --> <div class="legend" style=""> <div class="legend-item"> <span class="legend-symbol marker green"></span> <span class="legend-label">Green Line</span> </div> </div> <!-- Chart --> <div class="chart-container" style="width: 86%;"> <!-- Quantitative Scale --> <div class="tick-bar"> <div class="tick" style="left: 0%;"></div> <div class="tick-label" style="left: 0%;">0</div> <div class="tick" style="left: 25%;"></div> <div class="tick-label" style="left: 25%;">25</div> <div class="tick" style="left: 50%;"></div> <div class="tick-label" style="left: 50%;">50</div> <div class="tick" style="left: 75%;"></div> <div class="tick-label" style="left: 75%;">75</div> <div class="tick" style="left: 100%;"></div> <div class="tick-label" style="left: 100%;">100</div> </div> <!-- Bars --> <div class="bar" style="left: 0px; width: 75%;" bar-index="0"></div> <div class="bar blue" style="left: 0px; width: 50%;" bar-index="1"></div> <!-- Markers --> <div class="marker green" style="left: 80%;" marker-index="0"></div> <div class="marker red" style="left: 50%;" marker-index="1"></div> </div> </div>
Unser Widget, das wir jquery.bulletchart
nennen, generiert diesen HTML-Code dynamisch aus den bereitgestellten Daten. Das endgültige Widget kann auf der Demoseite angezeigt werden, die Sie von GitHub herunterladen können. Der Aufruf zum Erstellen des Widgets sollte folgendermaßen aussehen:
$('.chart').bulletchart({ size: 86, bars: [ { title: 'Projected Target', value: 75, css: '' }, { title: 'Actual Target', value: 50, css: 'blue' } ], markers: [ { title: 'Green Line', value: 80, css: 'green' }, { title: 'Minimum Threshold', value: 50, css: 'red' } ], ticks: [0, 25, 50, 75, 100] });
Alle Werte sind in Prozent angegeben. Die size
-Option kann verwendet werden, wenn mehrere Aufzählungsdiagramme mit relativer Größe nebeneinander platziert werden sollen. Mit der Option ticks
werden die Beschriftungen auf der Skala platziert. Die Markierungen und Balken werden als Array von Objektliteralen mit den Eigenschaften title
, value
und css
angegeben.
Erstellen des Widgets
Nachdem wir die Struktur des Widgets kennen, können wir es erstellen. Ein Widget wird erstellt, indem $.widget()
mit dem Namen des Widgets und einem Objekt aufgerufen wird, das seine Instanzmethoden enthält. Die genaue API sieht folgendermaßen aus:
jQuery.widget(name[, base], prototype)
Im Moment werden wir nur mit den Argumenten Name und Prototyp arbeiten. Für das Bulletchart sieht unser grundlegender Widget-Stub folgendermaßen aus:
$.widget('nt.bulletchart', { options: {}, _create: function () {}, _destroy: function () {}, _setOption: function (key, value) {} });
Es wird empfohlen, dass Sie Ihre Widget-Namen immer mit einem Namespace versehen. In diesem Fall verwenden wir 'nt.bulletchart'. Alle Widgets der jQuery-Benutzeroberfläche befinden sich unter dem Namespace 'ui'. Obwohl wir das Widget mit einem Namespace versehen, enthält der Aufruf zum Erstellen eines Widgets für ein Element nicht den Namespace. Um ein Aufzählungsdiagramm zu erstellen, rufen wir einfach $('# elem').bulletchart()
auf.
Die Instanzeigenschaften werden nach dem Namen des Widgets angegeben. Konventionell sollte allen privaten Methoden des Widgets das Präfix '_' vorangestellt werden. Es gibt einige spezielle Eigenschaften, die von der Widget-Factory erwartet werden. Dazu gehören die options
, _create
, _destroy
und _setOption
.
-
options
: Dies sind die Standardoptionen für das Widget -
_create
: Die Widget-Factory ruft diese Methode auf, wenn das Widget zum ersten Mal instanziiert wird. Dies wird verwendet, um das anfängliche DOM zu erstellen und Ereignishandler anzuhängen. -
_init
: Nach dem Aufruf von_create
ruft die Factory_init
auf. Dies wird im Allgemeinen verwendet, um das Widget auf den Ausgangszustand zurückzusetzen. Sobald ein Widget erstellt wurde, wird durch Aufrufen des einfachen Widget-Konstruktors, z. B.: $.bulletchart(), auch das Widget zurückgesetzt. Dies ruft intern_init
auf. -
_setOption
: Wird aufgerufen, wenn Sie eine Option im Widget festlegen, mit einem Aufruf wie:$('#elem').bulletchart('option', 'size', 100)
. Später werden wir andere Möglichkeiten zum Festlegen von Optionen im Widget sehen.
Erstellen des ersten DOM mit _create
Unser Bulletchart-Widget wird mit der Methode _create
zum Leben erweckt. Hier erstellen wir die Grundstruktur für das Diagramm. Die _create
-Funktion ist unten zu sehen. Sie werden feststellen, dass hier außer dem Erstellen des Containers der obersten Ebene nicht viel passiert. Die eigentliche Arbeit zum Erstellen des DOM für Balken, Markierungen und Häkchen erfolgt in der Methode _setOption
. Dies mag zunächst etwas kontraintuitiv erscheinen, aber es gibt einen triftigen Grund dafür.
_create: function () { this.element.addClass('bullet-chart'); // chart container this._container = $('<div class="chart-container"></div>') .appendTo(this.element); this._setOptions({ 'size': this.options.size, 'ticks': this.options.ticks, 'bars': this.options.bars, 'markers': this.options.markers }); }
Beachten Sie, dass die Balken, Markierungen und Häkchen auch durch Festlegen von Optionen im Widget geändert werden können. Wenn wir den Code für seine Konstruktion in _create
behalten würden, würden wir uns in _setOption
wiederholen. Durch Verschieben des Codes nach _setOption
und Aufrufen von _create
wird die Duplizierung entfernt und die Konstruktion zentralisiert.
Darüber hinaus zeigt Ihnen der obige Code eine andere Möglichkeit, Optionen für das Widget festzulegen. Mit der _setOptions
-Methode (beachten Sie den Plural) können Sie mehrere Optionen auf einmal festlegen. Intern führt die Factory für jede der Optionen individuelle Aufrufe von _setOption
durch.
Die _setOption
-Methode
Für das Aufzählungsdiagramm ist die Methode _setOption
das Arbeitspferd. Es behandelt die Erstellung der Markierungen, Balken und Häkchen sowie alle Änderungen, die an diesen Eigenschaften vorgenommen wurden. Dabei werden alle vorhandenen Elemente gelöscht und basierend auf dem neuen Wert neu erstellt.
Die _setOption
-Methode empfängt sowohl den Optionsschlüssel als auch einen Wert als Argumente. Der Schlüssel ist der Name der Option, der einem der Schlüssel in den Standardoptionen entsprechen sollte. Um beispielsweise die Balken im Widget zu ändern, rufen Sie Folgendes auf:
$('#elem').bulletchart('option', 'bars', [{ title: 'New Marker', value: 50 }])
Die _setOption
-Methode für das Bulletchart sieht folgendermaßen aus:
_setOption: function (key, value) { var self = this, prev = this.options[key], fnMap = { 'bars': function () { createBars(value, self); }, 'markers': function () { createMarkers(value, self); }, 'ticks': function () { createTickBar(value, self); }, 'size': function () { self.element.find('.chart-container') .css('width', value + '%'); } }; // base this._super(key, value); if (key in fnMap) { fnMap[key](); // Fire event this._triggerOptionChanged(key, prev, value); } }
Hier erstellen wir einen einfachen Hash des Optionsnamens für die entsprechende Funktion. Mit diesem Hash arbeiten wir nur an gültigen Optionen und ignorieren ungültige Optionen stillschweigend. Hier passieren zwei weitere Dinge: ein Aufruf von _super()
und das Auslösen des Ereignisses "Option geändert". Wir werden sie später in diesem Artikel betrachten.
Für jede der Optionen, die das DOM ändern, rufen wir eine bestimmte Hilfsmethode auf. Die Hilfsmethoden createBars
, createMarkers
und createTickBar
werden außerhalb der Widget-Instanzeigenschaften angegeben. Dies liegt daran, dass sie für alle Widgets gleich sind und nicht für jede Widget-Instanz einzeln erstellt werden müssen.
// Creation functions function createTickBar(ticks, widget) { // Clear existing widget._container.find('.tick-bar').remove(); var tickBar = $('<div class="tick-bar"></div>'); $.each(ticks, function (idx, tick) { var t = $('<div class="tick"></div>') .css('left', tick + '%'); var tl = $('<div class="tick-label"></div>') .css('left', tick + '%') .text(tick); tickBar.append(t); tickBar.append(tl); }); widget._container.append(tickBar); } function createMarkers(markers, widget) { // Clear existing widget._container.find('.marker').remove(); $.each(markers, function (idx, m) { var marker = $('<div class="marker"></div>') .css({ left: m.value + '%' }) .addClass(m.css) .attr('marker-index', idx); widget._container.append(marker); }); } function createBars(bars, widget) { // Clear existing widget._container.find('.bar').remove(); $.each(bars, function (idx, bar) { var bar = $('<div class="bar"></div>') .css({ left: 0, width: '0%' }) .addClass(bar.css) .attr('bar-index', idx) .animate({ width: bar.value + '%' }); widget._container.append(bar); }); }
Alle Erstellungsfunktionen arbeiten mit Prozentsätzen. Dadurch wird sichergestellt, dass das Diagramm beim Ändern der Größe des enthaltenen Elements gut fließt.
Die Standardoptionen
Ohne beim Erstellen des Widgets angegebene Optionen werden die Standardeinstellungen verwendet. Dies ist die Rolle der options
-Eigenschaft. Für das Bulletchart sehen unsere Standardoptionen folgendermaßen aus:
$.widget('nt.bulletchart', { options: { // percentage: 0 - 100 size: 100, // [{ title: 'Sample Bar', value: 75, css: '' }], bars: [], // [{ title: 'Sample Marker', value: 50, css: '' }], markers: [], // ticks -- percent values ticks: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100] }, ... }
Wir beginnen mit einer Größe von 100%, ohne Balken und Markierungen und mit Zecken, die alle 10% platziert werden. Mit diesen Standardeinstellungen sollte unser Aufzählungszeichen wie folgt aussehen:



Bisher haben wir gesehen, wie das Widget mit _create
erstellt und mit _setOption
aktualisiert wird. Es gibt eine andere Lebenszyklusmethode, die aufgerufen wird, wenn Sie ein Widget zerstören. Dies ist die _destroy
-Methode. Wenn Sie $('# elem').bulletchart('destroy')
aufrufen, ruft die Widget-Factory intern _destroy
auf Ihrer Widget-Instanz auf. Das Widget ist dafür verantwortlich, alles zu entfernen, was es in das DOM eingeführt hat. Dies kann Klassen und andere DOM-Elemente umfassen, die in der _create
-Methode hinzugefügt wurden. Dies ist auch ein guter Ort, um Event-Handler zu lösen. Das _destroy
sollte das genaue Gegenteil der _create
-Methode sein.
Für das Bullet-Chart-Widget ist das _destroy
ganz einfach:
_destroy: function () { this.element.removeClass('bullet-chart'); this.element.empty(); },
Unterklassen, Ereignisse und mehr
Unser Bulletchart-Widget ist bis auf eine letzte Funktion fast vollständig: die Legende. Die Legende ist sehr wichtig, da sie den Markierungen und Balken mehr Bedeutung verleiht. In diesem Abschnitt fügen wir neben dem Diagramm eine Legende hinzu.
Anstatt diese Funktion direkt zum Bulletchart-Widget hinzuzufügen, erstellen wir eine Unterklasse, bulletchart2
, die die Legendenunterstützung bietet. Dabei werden wir uns auch einige der interessanten Funktionen der Widget Factory-Vererbung ansehen.
Hinzufügen einer Legende



Die Widget Factory unterstützt die Unterklasse eines Widgets, um speziellere Versionen zu erstellen. Zu Beginn des Artikels haben wir die API für $.widget()
gesehen, die drei Argumente hatte:
jQuery.widget (name[, base], prototype)
Mit dem zweiten Parameter können wir eine Basisklasse für unser Widget auswählen. Unser bulletchart2
-Widget, das bulletchart
unterordnet, hat die folgende Signatur:
$.widget('nt.bulletchart2', $.nt.bulletchart, { options: { // Show/hide legend legend: true }, // this ensures we keep the same namespace as the base widgetEventPrefix: $.nt.bulletchart.prototype.widgetEventPrefix, _create: function () { ... }, _destroy:function(){ ... }, _setOption: function (key, value) { ... } })
Hier sind einige interessante Dinge zu beachten:
- Wir setzen weiterhin den Namespace unseres Widget-Namens:
nt.bulletchart2
. - Die Widget-Factory platziert das Widget automatisch unter dem Namespace $.nt. Um auf unser vorheriges Widget zu verweisen, haben wir
$.nt.bulletchart
verwendet. Wenn wir eines der Standard-Widgets der jQuery-Benutzeroberfläche unterklassifizieren würden, würden wir sie mit$.ui.widget-name
referenzieren - Das
widgetEventPrefix
ist eine neue Eigenschaft, die wir noch nicht gesehen haben. Wir werden darauf zurückkommen, wenn wir über Ereignisse sprechen. Die restlichen Instanzeigenschaften sollten bekannt sein.
Da wir der Legende weitere DOM-Elemente hinzufügen, müssen wir die Methode _create
überschreiben. Dies bedeutet auch, dass wir _destroy
überschreiben müssen, um symmetrisch zu sein.
_create: function () { var self = this; this._legend = $('<div class="legend"></div>') .appendTo(this.element); ... // Call the base this._super(); this._setOption('legend', this.options.legend); }, _destroy:function(){ this.element.find('.legend').empty(); ... this._super(); },
Auch hier sehen wir das gleiche Muster wie bei unserer früheren Methode _create
. Wir erstellen den Container für die Legende und rufen dann _setOption
auf, um den Rest der Legende zu erstellen. Da wir _create
überschreiben, müssen wir sicherstellen, dass wir die Basis _create
aufrufen. Wir machen das mit dem Aufruf an _super
. In ähnlicher Weise sehen wir in _destroy
auch den Aufruf zu _super
.
Jetzt fragen Sie sich vielleicht: Woher weiß das Widget, welche Supermethode mit einem einfachen, nicht qualifizierten _super
-Aufruf aufgerufen werden soll? Die Intelligenz dafür liegt im Darm der Widget-Fabrik. Wenn ein Widget in Unterklassen unterteilt ist, richtet die Factory die _super
-Referenz für jede Instanzfunktion unterschiedlich ein. Wenn Sie also _super
von Ihrer Instanzmethode aus aufrufen, zeigt dies immer auf die richtige _super
-Methode.
Ereignisbenachrichtigungen
Da das Bulletchart das Ändern von Markierungen und Balken unterstützt, muss die Legende mit diesen Änderungen synchronisiert sein. Darüber hinaus unterstützen wir das Umschalten der Sichtbarkeit von Markierungen und Balken durch Klicken auf die Legendenelemente. Dies ist nützlich, wenn Sie mehrere Markierungen und Balken haben. Wenn Sie einige der Elemente ausblenden, können Sie die anderen klarer sehen.
Um die Synchronisierung der Legende mit den Änderungen an Markierungen und Balken zu unterstützen, muss das bulletchart2
-Widget alle Änderungen an diesen Eigenschaften abhören. Das Basis-Bulletchart löst bereits jedes Mal ein Änderungsereignis aus, wenn sich seine Optionen ändern. Hier ist das entsprechende Snippet aus dem Basis-Widget:
_setOption: function (key, value) { var self = this, prev = this.options[key]; ... // base this._super(key, value); if (key in fnMap) { fnMap[key](); // Fire event this._triggerOptionChanged(key, prev, value); } }, _triggerOptionChanged: function (optionKey, previousValue, currentValue) { this._trigger('setOption', {type: 'setOption'}, { option: optionKey, previous: previousValue, current: currentValue }); }
Immer wenn eine Option gesetzt ist, wird das setOption
-Ereignis ausgelöst. Die Ereignisdaten enthalten den vorherigen und neuen Wert für die Option, die geändert wurde.
Wenn Sie dieses Ereignis im untergeordneten Widget abhören, können Sie feststellen, wann sich die Markierungen oder Balken ändern. Das bulletchart2
-Widget abonniert dieses Ereignis in seiner _create
-Methode. Das Abonnieren von Widgets-Ereignissen wird mit dem Aufruf von this.element.on()
erreicht. this.element
zeigt auf das jQuery-Element, auf dem das Widget instanziiert wurde. Da das Ereignis auf das Element ausgelöst wird, muss unser Ereignisabonnement darauf erfolgen.
_create: function () { var self = this; this._legend = $('<div class="legend"></div>') .appendTo(this.element); ... // Apply legend on changes to markers and bars 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); } }); // Call the base this._super(); this._setOption('legend', this.options.legend); }
Beachten Sie den Ereignisnamen, der zum Abonnieren verwendet wird: 'bulletchart: setoption'
. Als Richtlinie fügt die Widget-Factory ein Ereignispräfix für Ereignisse hinzu, die vom Widget ausgelöst werden. Standardmäßig ist dieses Präfix der Name des Widgets. Dies kann jedoch einfach mit der Eigenschaft widgetEventPrefix
geändert werden. Das Basis-Bulletchart-Widget ändert dies in 'bulletchart:'
.
$.widget('nt.bulletchart', { options: { ... }, widgetEventPrefix: 'bulletchart:' ... });
Wir müssen auch 'click'
-Ereignisse für die Legendenelemente abonnieren, um die entsprechende Markierung / Leiste auszublenden / anzuzeigen. Wir machen das mit der _on
-Methode. Diese Methode überträgt einen Hash der Ereignissignatur an die Handlerfunktion. Der Kontext des Handlers (this
) ist korrekt auf die Widget-Instanz eingestellt. Ein weiterer Vorteil von _on
ist, dass die Widget-Factory die Ereignisse beim Zerstören automatisch aufhebt.
_create: function () { ... // Listen to clicks on the legend-items 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('fade'); } }); ... }
Mehr Tipps
Die Widget-Fabrik bietet einige weitere Besonderheiten, die Sie beachten sollten.
Verweisen auf die Widget-Instanz
Bisher haben wir nur eine Möglichkeit gesehen, Methoden im Widget aufzurufen. Wir haben dies mit $('#elem) .bulletchart('method-name')
gemacht. Dies erlaubt jedoch nur das Aufrufen öffentlicher Methoden wie 'Option', 'Zerstören', 'Ein', 'Aus'. Wenn Sie diese Methoden direkt in der Widget-Instanz aufrufen möchten, gibt es eine Möglichkeit, dies zu tun. Die Widget-Factory hängt die Widget-Instanz an das data()
-Objekt des Elements an. Sie können diese Instanz folgendermaßen erhalten:
var widget = $('#elem').data('bulletchart'); widget.destroy();
Wenn Sie alle Bulletchart-Widgets auf der Seite abrufen möchten, gibt es außerdem eine Auswahl dafür:
var allCharts = $(':nt-bulletchart');
Einige spezielle Methoden
Es gibt einige spezielle Methoden, die Sie kennen sollten und die weniger häufig verwendet werden: _getCreateEventData()
und _getCreateOptions()
. Ersteres wird verwendet, um Ereignisdaten für das Ereignis 'create' anzuhängen, das nach Beendigung des Aufrufs von _create
ausgelöst wird.
_getCreateOptions
dient zum Anhängen zusätzlicher Standardoptionen für das Widget oder zum Überschreiben vorhandener Optionen. Die vom Benutzer bereitgestellten Optionen überschreiben die von dieser Methode zurückgegebenen Optionen, wodurch wiederum die Standard-Widget-Optionen überschrieben werden.
Zusammenfassung
Das ist ein Wrap! Wenn Sie weiter erforschen möchten, sollten die folgenden Referenzen Ihnen recht gut dienen. Die beste Informationsquelle ist natürlich immer der Quellcode selbst. Ich würde empfehlen, die Quelle jquery.ui.widget auf GitHub zu lesen.