Скачать плагин можно здесь https://github.com/freeart/observable
Что нового/интересного?
Для начала краткая историческая справка, плагин создавался для упрощения привязки функций-обработчиков к представлению.
Первая версия плагина работа весьма просто.
Есть такая dom-структура
<div role="document" class="container">… и бизнес-объект
…
<span class="run" data-fn="run" data-event="click">Run it!</span>
<span class="stop" data-fn="stop" data-event="click">Stop!</span>
…
</div>
var testWorker = {
run: function(e){
//..run code
},
stop: function(e){
//..stop code
}
}
Плагин Observable ищет в ветке [role="document"] все декларации событий в виде data-параметров. data-fn = “имя функции-обработчика в указанном объекте testWorker” и data-event = “имя события – в данном примере click” – обязательные параметры. Также существуют необязательные параметры data-pref, data-scope, data-selector, data-type, но о них позже.
Это примерно эквивалентно
$('[role="document"]').delegate(".run", "click", function(e){ //..run code });
$('[role="document"]').delegate(".stop", "click", function(e){
//..stop code
});
Зачем же нужен этот плагин, если и так все работает замечательно!?
- Заставляет использовать бизнес-объекты с публичными методами, которые хорошо организуют код.
Например методы run/stop объекта testWorker создают более понятный код, чем в случае с анонимными обработчиками. - Отделяет логику от представления: дизайнер рисует, верстальщик верстает, программист привязывает обработчики к UI и описывает логику приложения.
Посмотрите в сторону Objective-C, Flash, WPF. - Для привязки функций-обработчиков не используются атрибуты id или class – дизайн никак не пересекается с логикой приложения.
Например имя класса .run вестальщик может изменить на что-то более ему понятное/удобное = .button, а программист получает нерабочий код. А декларация в виде data-атрибутов никому не мешает. - Упрощает код, на небольшом количестве обработчиков этого конечное не понять, а вот когда количество обработчиков превышает 20-30 шт. - код превращается в неуправляемую массу.
- Не анонимные методы в качестве обработчиков заставляют писать заведомо правильный и масштабируемый код. Бизнес-объект testWorker имеет метод run, и может быть вызван не только по нажатие на кнопку “run it!”, но и по другому условию, например при загрузке страницы.
$.ready(function(){ testWorker.run() });
- Функции-обработчики приходится “группировать” – получая в итоге привязку одной бизнес-единицы к представлению.
$('[role="document"]').observable(testWorker);
С точки зрения семантики эта строчка означает привязку бизнес-объекта testWorker к представлению document.
Начиная с версии Observable 1.3 стали поддерживаться конструкции маппинга функций-обработчиков вторым параметром. Он используется в случае когда у вас dom-структура еще не загружена.
$('[role="document"]').observable(testWorker, {
"data-pref": "run,stop",
"data-fn-run": "run",
"data-event-run": "click",
"data-fn-stop": "run",
"data-event-stop": "click"
});
Вот и дошли до этого атрибута data-pref. Т.к. data-параметры не поддерживают массивы, то для объединения data-fn с data-event используется такой синтаксис:
"data-pref": "run",
"data-fn-run": "run",
"data-event-run": "click"
Синтаксис полностью совместимый с dom версией, но несколько неуклюж и громоздок.
Поэтому версия Observable 1.4 получила поддержку сокращенного синтаксиса:
$('[role="document"]').observable(testWorker, [
{
fn: "run",
event: "click"
},
{
fn: "stop",
event: "click"
}
]);
В случае если вы по каким-то соображениям не хотите в верстке использовать data-параметры, вы можете использовать поле selector, и по старинке привязать обработчик по селектору.
$('[role="document"]').observable(testWorker, [ {
fn: "run", event: "click",
selector: ".run" //эквивалентен $("[role="document"]").delegate(".run", "click", testWorker.run) }, {
fn: "stop", event: "click",
selector: ".stop" //эквивалентен $("[role="document"]").delegate(".stop", "click", testWorker.stop) } ]);
Остались два параметра data-scope и data-type. По умолчанию привязка обработчиков происходит через delegate, при желании вы можете указать bind. Но нужно понимать что bind можно привязать только если этот элемент присутствует в dom-дереве на момент.
Все функции-обработчики выполняются как обычные обработчики dom элементов в контексте dom-элемента, т.е. this == элемент, который сгенерировал событие, в наших примерах это span dom элементы .run и .stop . Если вы хотите использовать свой контекст, например того бизнес объекта, который вы передаете в метод observable, т.е. testWorker, в этом случае указываем data-scope = "this", или любой другой в рамках testWorker, т.к. ссылку на имя контекста scope ищется от нашего ROOT объекта testWorker.
$('[role="document"]').observable(testWorker, [ {
fn: "run", event: "click",
scope: "this"
}, {
fn: "stop", event: "click",
scope: "this" } ]);
Методы run и stop в этом случае вызывается таким образом:
fn.call(testWorker, event, thisElement, testWorker);
Что почти эквивалентно такому приему:
$('[role="document"]').gelegate("click", ".run", function(e){ //..run code }.bind(testWorker));
var testWorker = {
run: function(e, element, obj){
//..run code
// для справки: this === element, obj === testWorker
console.log(this);
},
stop: function(e, element, obj){
//..stop code
console.log(this);
}
}
$('[role="document"]').observable(testWorker);
No comments:
Post a Comment