Долгое время единственным способом написания пользовательских элементов управления в jQuery было расширение пространства имен $.fn
. Это хорошо работает для простых виджетов, однако, как только вы начинаете создавать виджеты с большим состоянием, они быстро становятся громоздкими. Чтобы помочь в процессе создания виджетов, команда пользовательского интерфейса jQuery представила Фабрику виджетов, которая удаляет большую часть стандартного шаблона, который обычно связан с управлением виджетом.
Фабрика виджетов, являющаяся частью jQuery UI Core , предоставляет объектно-ориентированный способ управления жизненным циклом виджета. Эти жизненные циклы включают в себя:
- Создание и уничтожение виджета
- Изменение параметров виджета
- Выполнение « супер » вызовов в подклассах виджетов
- Уведомления о событиях
Давайте рассмотрим этот API, так как мы создадим простой виджет маркера.
Пуля Диаграмма Виджет
Прежде чем мы создадим этот виджет, давайте разберемся с некоторыми строительными блоками виджета. Bullet Chart — это концепция, представленная Стивеном Фью как разновидность гистограммы.
Диаграмма состоит из набора полос и маркеров, наложенных друг на друга, чтобы показать относительную производительность. Существует количественная шкала для отображения фактического диапазона значений. Располагая столбцы и маркеры таким образом, можно передавать больше информации без ущерба для читабельности. Легенда рассказывает, какую информацию мы собираем.
HTML-код для этой диаграммы выглядит так:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
<!— Chart Container —>
<div class=»chart bullet-chart»>
<!— Legend —>
<div class=»legend» style=»»>
<div class=»legend-item»>
<span class=»legend-symbol marker green»>
<span class=»legend-label»>Green Line
</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%;»
<div class=»bar blue» style=»left: 0px; width: 50%;»
<!— Markers —>
<div class=»marker green» style=»left: 80%;»
<div class=»marker red» style=»left: 50%;»
</div>
</div>
|
Наш виджет, который мы назовем jquery.bulletchart
, будет динамически генерировать этот HTML из предоставленных данных. Окончательный виджет можно посмотреть в исходных файлах, которые вы можете скачать с GitHub. Вызов для создания виджета должен выглядеть так:
$ ( 'График'). Bulletchart ({ размер: 86, бары: [ {title: 'Projected Target', значение: 75, css: ''}, {title: 'Actual Target', значение: 50, css: 'blue'} ], маркеры: [ {title: 'Green Line', значение: 80, css: 'green'}, {title: 'Minimum Threshold', значение: 50, css: 'red'} ], галочки: [0, 25, 50, 75, 100] });
Все значения в процентах. Опцию size
можно использовать, когда вы хотите, чтобы несколько диаграмм располагались рядом друг с другом с относительным размером. Опция ticks
используется для размещения меток на шкале. Маркеры и столбцы указываются в виде массива литералов объектов со свойствами title
, value
и css
.
Создание виджета
Теперь, когда мы знаем структуру виджета, давайте приступим к его созданию. Виджет создается путем вызова $.widget()
с именем виджета и объекта, содержащего методы его экземпляра. Точный API выглядит так:
jQuery.widget(name[, base], prototype)
Сейчас мы будем работать только с аргументами name и prototype. Для маркированной таблицы наша основная заглушка виджета выглядит следующим образом:
$ .widget ('nt.bulletchart', { параметры: {}, _create: function () {}, _destroy: function () {}, _setOption: функция (ключ, значение) {} });
Рекомендуется всегда использовать пространство имен для имен виджетов. В этом случае мы используем ‘ nt.bulletchart ‘. Все виджеты jQuery UI находятся в пространстве имен ‘ ui ‘. Хотя мы создаем пространство имен для виджета, вызов для создания виджета на элементе не включает пространство имен. Таким образом, чтобы создать диаграмму $('#elem').bulletchart()
, мы бы просто $('#elem').bulletchart()
.
Свойства экземпляра указываются после имени виджета. По соглашению, все закрытые методы виджета должны начинаться с префикса ‘_’. Есть некоторые специальные свойства, которые ожидаются фабрикой виджетов. К ним относятся options
, _create
, _destroy
и _setOption
.
-
options
: это параметры по умолчанию для виджета -
_create
: Фабрика виджетов вызывает этот метод при первом создании экземпляра виджета. Это используется для создания исходного DOM и присоединения любых обработчиков событий. -
_init
: после вызова_create
, фабрика вызывает_init
. Обычно используется для сброса виджета в исходное состояние. Как только виджет создан, вызов конструктора простого виджета, например: $ .bulletchart () , также сбросит виджет. Это внутренне вызывает_init
. -
_setOption
: Вызывается, когда вы устанавливаете опцию в виджете, с помощью вызова, такого как:$('#elem').bulletchart('option', 'size', 100)
. Позже мы увидим другие способы настройки параметров в виджете.
Создание исходного DOM с помощью _create
Наш виджет Bulletchart оживает в методе _create
. Вот где мы строим базовую структуру для диаграммы. _create
можно увидеть ниже. Вы заметите, что здесь мало что происходит, кроме создания контейнера верхнего уровня. Фактическая работа по созданию DOM для баров, маркеров и тиков происходит в методе _setOption
. Это может показаться несколько нелогичным, но для этого есть веская причина.
_create: function () { this.element.addClass ( 'пуля-диаграмма'); // график контейнера this._container = $ ('<div class = "chart-container"> </ div>') .appendTo (this.element); this._setOptions ({ «размер»: this.options.size, 'ticks': this.options.ticks, 'bars': this.options.bars, 'маркеры': this.options.markers }); }
Обратите внимание, что столбцы, маркеры и отметки также можно изменить, установив параметры в виджете. Если бы мы сохранили код для его построения внутри _create
, мы бы повторяли себя внутри _setOption
. Перемещая код в _setOption
и вызывая его из _create
удаляет дублирование, а также централизуется конструкция.
Кроме того, приведенный выше код демонстрирует другой способ настройки параметров виджета. С _setOptions
метода _setOptions
(обратите внимание на множественное число) вы можете установить несколько параметров за один раз. Внутри фабрика будет выполнять отдельные вызовы _setOption
для каждого из параметров.
Метод _setOption
Для _setOption
метод _setOption
является рабочей лошадкой. Он обрабатывает создание маркеров, баров и тиков, а также любые изменения, внесенные в эти свойства. Он работает путем очистки любых существующих элементов и воссоздания их на основе нового значения.
Метод _setOption
получает ключ опции и значение в качестве аргументов. Ключ — это имя параметра, которое должно соответствовать одному из ключей в параметрах по умолчанию. Например, чтобы изменить столбцы в виджете, вы должны сделать следующий вызов:
$ ('# elem'). bulletchart ('option', 'bars', [{ title: 'New Marker', значение: 50 }])
Метод _setOption
для маркера выглядит так:
_setOption: функция (ключ, значение) { var self = this, prev = this.options [ключ], fnMap = { 'bars': function () { createBars (value, self); }, 'маркеры': функция () { createMarkers (value, self); }, 'ticks': function () {createTickBar (value, self); }, 'размер': функция () { self.element.find ( 'график-контейнер') .css ('ширина', значение + '%'); } }; // база this._super (ключ, значение); if (введите fnMap) { fnMap [ключ] (); // Пожарное событие this._triggerOptionChanged (ключ, пред, значение); } }
Здесь мы создаем простой хэш имени опции для соответствующей функции. Используя этот хэш, мы работаем только с допустимыми параметрами и игнорируем недействительные. Здесь происходит еще две вещи: вызов _super()
и запуск события _super()
option. Мы рассмотрим их позже в этой статье.
Для каждого из параметров, которые изменяют DOM, мы вызываем определенный вспомогательный метод. Вспомогательные методы createBars
, createMarkers
и createTickBar
указываются вне свойств экземпляра виджета. Это потому, что они одинаковы для всех виджетов и не должны создаваться отдельно для каждого экземпляра виджета.
// Создание функций функция createTickBar (тики, виджет) { // Очистить существующий widget._container.find ( 'тик-бар') удалить (). var tickBar = $ ('<div class = "tick-bar"> </ div>'); $ .each (тики, функция (idx, тик) { var t = $ ('<div class = "tick"> </ div>') .css ('left', tick + '%'); var tl = $ ('<div class = "tick-label"> </ div>') .css ('left', tick + '%') .text (клещ); tickBar.append (т); tickBar.append (Tl); }); widget._container.append (tickBar); } функция createMarkers (маркеры, виджет) { // Очистить существующий widget._container.find ( 'Маркер') удалить (). $ .each (маркеры, функция (idx, m) { var marker = $ ('<div class = "marker"> </ div>') .css ({слева: m.value + '%'}) .addClass (m.css) .attr ('marker-index', idx); widget._container.append (маркер); }); } функция createBars (бары, виджет) { // Очистить существующий widget._container.find ( 'бар') удалить (). $ .each (бары, функция (idx, бар) { var bar = $ ('<div class = "bar"> </ div>') .css ({слева: 0, ширина: '0%'}) .addClass (bar.css) .attr ('bar-index', idx) .animate ({ ширина: bar.value + '%' }); widget._container.append (бар); }); }
Все функции создания работают в процентах. Это гарантирует, что диаграмма хорошо изменится при изменении размера содержащего элемента.
Параметры по умолчанию
Без каких-либо параметров, указанных при создании виджета, будут действовать значения по умолчанию. Это роль свойства options
. Для маркера наши параметры по умолчанию выглядят так:
$ .widget ('nt.bulletchart', { параметры: { // процент: 0 - 100 размер: 100, // [{title: 'Sample Bar', значение: 75, css: ''}], бары: [], // [{title: 'Sample Marker', значение: 50, css: ''}], маркеры: [], // галочки - процентные значения галочки: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100] }, ... }
Мы начинаем с размера 100% , без баров и маркеров и с тиками, размещаемыми каждые 10% . С этими значениями по умолчанию наша диаграмма маркера должна выглядеть так:
До сих пор мы видели, как создать виджет с помощью _create
и обновить его с помощью _setOption
. Существует еще один метод жизненного цикла, который будет вызываться при уничтожении виджета. Это метод _destroy
. Когда вы вызываете $('#elem').bulletchart('destroy')
, фабрика виджетов внутренне вызывает _destroy
для вашего экземпляра виджета. Виджет отвечает за удаление всего, что он ввел в DOM. Это может включать в себя классы и другие элементы DOM, которые были добавлены в метод _create
. Это также хорошее место, чтобы отменить привязку любых обработчиков событий. _destroy
должен быть полной противоположностью метода _create
.
Для виджета с _destroy
довольно прост:
_destroy: function () { this.element.removeClass ( 'пуля-диаграмма'); this.element.empty (); },
Подклассы, события и многое другое
Наш виджет Bulletchart почти завершен, за исключением одной последней функции: легенды . Легенда очень важна, так как она придаст больше значения маркерам и полосам. В этом разделе мы добавим легенду рядом с диаграммой.
Вместо того, чтобы добавлять эту функцию непосредственно в виджет Bulletchart, мы создадим подкласс bulletchart2
, который будет иметь поддержку легенды. В процессе мы также рассмотрим некоторые интересные особенности наследования Widget Factory.
Добавление легенды
Фабрика виджетов поддерживает создание подклассов виджетов для создания более специализированных версий. Ранее в статье мы видели API для $.widget()
, который имел три аргумента:
jQuery.widget(name[, base], prototype)
Второй параметр позволяет нам выбрать базовый класс для нашего виджета. Наш виджет bulletchart2
, который является подклассом bulletchart
, будет иметь следующую подпись:
$ .widget ('nt.bulletchart2', $ .nt.bulletchart, { параметры: { // Показать / скрыть легенду легенда: правда }, // это гарантирует, что мы сохраняем то же пространство имен, что и база widgetEventPrefix: $ .nt.bulletchart.prototype.widgetEventPrefix, _create: function () {...}, _destroy: function () {...}, _setOption: функция (ключ, значение) {...} })
Здесь есть несколько интересных вещей:
- Мы продолжаем пространство имен нашего имени виджета:
nt.bulletchart2
. - Фабрика виджетов автоматически помещает виджет в пространство имен $ .nt . Таким образом, для ссылки на наш предыдущий виджет мы использовали
$.nt.bulletchart
. Точно так же, если бы мы$.ui.widget-name
подкласс одного из стандартных виджетов jQuery UI, мы бы указали на них с$.ui.widget-name
-
widgetEventPrefix
— это новое свойство, которого мы раньше не видели. Мы вернемся к этому, когда будем говорить о событиях. Остальные свойства экземпляра должны быть знакомы.
Поскольку мы добавляем больше элементов DOM с легендой, нам придется переопределить метод _create
. Это также означает, что нам нужно переопределить _destroy
, чтобы быть симметричным.
_create: function () { var self = this; this._legend = $ ('<div class = "legend"> </ div>') .appendTo (this.element); ... // Вызов базы this._super (); this._setOption ('legend', this.options.legend); }, _destroy: функция () { this.element.find ( 'легенда'.) опорожнить (). ... this._super (); },
Здесь мы снова видим тот же шаблон, что и наш предыдущий метод _create
. Мы создаем контейнер для легенды, а затем вызываем _setOption
для создания остальной части легенды. Поскольку мы переопределяем _create
, нам нужно убедиться, что мы вызываем базовый _create
. Мы делаем это с помощью вызова _super
. Точно так же в _destroy
мы также видим вызов _super
.
Теперь вы можете задаться вопросом: как виджет узнает, какой супер-метод вызывать с помощью простого неквалифицированного вызова _super
? Смарты для этого лежат в недрах фабрики виджетов. Когда виджет находится в подклассе, фабрика по- _super
ссылку _super
для каждой из функций экземпляра. Таким образом, когда вы вызываете _super
из вашего метода экземпляра, он всегда указывает на правильный метод _super
.
Уведомления о событиях
Поскольку сводная диаграмма поддерживает изменение маркеров и полос, легенда должна синхронизироваться с этими изменениями. Кроме того, мы также будем поддерживать переключение видимости маркеров и полос, щелкая элементы легенды. Это становится полезным, когда у вас есть несколько маркеров и баров. Скрывая несколько элементов, вы можете видеть другие более четко.
Для поддержки синхронизации легенды с изменениями маркеров и полос виджет bulletchart2
должен прослушивать любые изменения, происходящие с этими свойствами. Базовая диаграмма уже запускает событие change каждый раз, когда меняются его параметры. Вот соответствующий фрагмент из базового виджета:
_setOption: функция (ключ, значение) { var self = this, prev = this.options [ключ]; ... // база this._super (ключ, значение); if (введите fnMap) { fnMap [ключ] (); // Пожарное событие this._triggerOptionChanged (ключ, пред, значение); } }, _triggerOptionChanged: function (optionKey, previousValue, currentValue) { this._trigger ('setOption', {type: 'setOption'}, { option: optionKey, previous: previousValue, current: currentValue }); }
Всякий раз, когда опция установлена, setOption
событие setOption
. Данные события содержат предыдущее и новое значение для опции, которая была изменена.
Прослушивая это событие в подклассе виджета, вы можете узнать, когда меняются маркеры или столбцы. Виджет bulletchart2
подписывается на это событие в своем методе _create
. Подписка на события виджетов осуществляется с помощью вызова this.element.on()
. this.element
указывает на элемент jQuery, для которого был создан экземпляр виджета. Поскольку событие будет запущено для элемента, наша подписка на событие должна произойти на этом.
_create: function () { var self = this; this._legend = $ ('<div class = "legend"> </ div>') .appendTo (this.element); ... // Применяем легенду об изменениях маркеров и баров 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); } }); // Вызов базы this._super (); this._setOption ('legend', this.options.legend); }
Обратите внимание на название события, используемого для подписки: 'bulletchart:setoption'
. В качестве политики фабрика виджетов присоединяет префикс события к событиям, инициируемым из виджета. По умолчанию этот префикс является именем виджета, но его можно легко изменить с widgetEventPrefix
свойства widgetEventPrefix
. Базовый виджет Bulletchart меняет это на 'bulletchart:'
.
$ .widget ('nt.bulletchart', { параметры: { ... }, widgetEventPrefix: 'bulletchart:' ... });
Нам также необходимо подписаться на события 'click'
на элементах легенды, чтобы скрыть / показать соответствующий маркер / панель. Мы делаем это с _on
метода _on
. Этот метод передает хэш сигнатуры события в функцию-обработчик. Контекст обработчика ( this
) правильно установлен на экземпляр виджета. Еще одно удобство с _on
состоит в том, что фабрика виджетов автоматически отменяет привязку событий при разрушении.
_create: function () { ... // Слушаем клики по элементам легенды this._on ({ 'click .legend-item': функция (событие) { var elt = $ (event.currentTarget), item = elt.data ('chart-item'), selector = '[' + item.type + '-index =' + item.index + ']'; this.element.find (селектор) .fadeToggle (); elt.toggleClass ( 'замирания'); } }); ... }
Дополнительные советы
Фабрика Widget включает в себя несколько других тонкостей, о которых вы должны знать.
Ссылка на экземпляр виджета
До сих пор мы видели только один способ вызова методов в виджете. Мы сделали это с помощью $('#elem).bulletchart('method-name')
. Однако это позволяет только вызывать открытые методы, такие как ‘option’, ‘destroy’, ‘on’, ‘off’. Если вы хотите вызывать эти методы непосредственно в экземпляре виджета, есть способ сделать это. Фабрика виджетов присоединяет экземпляр виджета к объекту data()
элемента. Вы можете получить этот экземпляр так:
var widget = $ ('# elem'). data ('bulletchart'); widget.destroy ();
Кроме того, если вы хотите получить доступ ко всем виджетам Bulletchart на странице, есть также селектор для этого:
var allCharts = $ (': nt-bulletchart');
Некоторые специальные методы
Есть несколько специальных методов, о которых вам следует знать, которые используются реже: _getCreateEventData()
и _getCreateOptions()
. Первый используется для прикрепления данных события для события ‘create’, которое запускается после завершения вызова _create
.
_getCreateOptions
— для добавления дополнительных параметров по умолчанию для виджета или переопределения существующих. Пользовательские параметры переопределяют параметры, возвращаемые этим методом, который, в свою очередь, переопределяет параметры виджета по умолчанию.
Резюме
Это упаковка! Если вы хотите изучить дальше, ссылки ниже должны служить вам довольно хорошо. Конечно, лучшим источником информации всегда будет сам исходный код. Я бы рекомендовал прочитать исходный код jquery.ui.widget на GitHub.