Wednesday, September 21, 2011

Новая версия плагина Observable 1.4

sticker

Скачать плагин можно здесь 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