webew
Войти » Регистрация
 
HTML
JavaScript :: События
JavaScript :: AJAX

Согласованный AJAX-слой для веб-сайта

30 декабря 2014, 10:26

Реакцией на действие пользователя может быть согласованное изменение содержимого нескольких блоков на странице, происходящее без её перезагрузки. Вниманию читателей предлагается простой программный интерфейс для обновления указанных блоков (всех или части), который позволяет сформировать из них единый согласованный AJAX-слой.

Чтобы реализовать на сайте согласованный AJAX-слой, нужно:

  • составить список взаимно согласованных блоков
  • подключить небольшую javascript-библиотеку и установить на сайт специальный код1
  • подготовить серверную часть для AJAX-ответа

Ярким примером сайта, которому необходим согласованный AJAX-слой, является интернет-магазин. Для него мы и рассмотрим технику внедрения.

Определение состава AJAX-слоя

Первый этап внедрения AJAX-слоя — понять, какие же, собственно, блоки в него входят и в каких случаях они должны обновляться. Для включения в слой каждому блоку требуется назначить имя (любая строка). Такой анализ нужно провести для всех страниц сайта, при этом имена блоков должны быть уникальными в рамках всего слоя.

Типичный интернет-магазин имеет практически на всех страницах:

  1. Блок с общей информацией о содержимом корзины — назовем его cart:brief.
    Этот блок должен обновляться при изменении состава покупок: нажатии кнопки «купить», авторизации пользователя, у которого была непустая корзина и т.д.
  2. Зону авторизации: форма, кнопка «войти» и пр., или же, для авторизованных пользователей, некую личную информацию (приветствие и пр.) — auth:brief.
    Обновление требуется при успешной авторизации или регистрации пользователя.
  3. Список товарных предложений, каждое из которых снабжено кнопкой «купить» или надписью «товар в корзине». Участником слоя в данном случае является контейнер кнопки, ему дадим имя вида buy:{id товара} (имя тогда будет уникальным, даже если на странице присутствует несколько товаров).
    Наличие кнопки должно согласовываться с присутствием товара в корзине и обновляться при изменении списка покупок.

Приписание блокам их имен осуществляется посредством установки значения атрибуту data-*. Конкретное имя атрибута можно выбирать произвольно, главное, чтобы у всех элементов оно было одинаковым2.

В случае примера можно сказать, что мы имеем дело с неким персональным слоем покупателя, поэтому назвать атрибут можно, например, data-personal-ajax. В HTML-код перечисленных выше блоков тогда нужно добавить атрибуты data-personal-ajax="cart:brief", data-personal-ajax="auth:brief" и т.д.

Установка кода слоя на страницу. Программный интерфейс для обновления.

Для инициализации слоя на странице нужно подключить код библиотеки и создать объект слоя, который следует записать в глобальную переменную для использования в замыканиях. Это стоит сделать пораньше (например, в <head>).

В случае персонального слоя для интернет-магазина код может выглядеть так:

var PERSONAL_AJAX_LAYER = new AjaxLayer({
        'html_data_name': 'personal-ajax', // имя data-атрибута
        'http_query_key': 'personal_ajax', // ключ для AJAX-запросов
        'server': '/ajax-layer.php',       // адрес для AJAX-запросов
});

(Параметры http_query_key и server описаны в разделе, посвященном серверной части.)

Для обновления слоя нужно вызывать метод .refresh(), указывая имена блоков, которые следует обновить, в виде массива. Например:

  • .refresh( ['cart', 'buy'] ) — обновить все блоки, имя которых начинается с cart или buy
  • .refresh( [/^(cart|buy)/] ) — то же самое, но в виде регулярного выражения
  • .refresh( ['buy*23570'] ) — обновить блоки, имя которых начинается на buy и затем содержит в себе 23570
  • .refresh('*') — полностью обновить AJAX-слой на данной странице

Вызов метода .refresh() приводит к запросу с сервера актуального HTML-кода перечисленных блоков. (Подробности см. в разделе о серверной части.)

Интеграция с обработчиками событий

В типичном случае события, которые должны вызывать обновление слоя, уже содержат отправку AJAX-запроса, снабженную собственной callback-функцией. Для интеграции слоя в обработчик нужно добавить внутрь этой функции вызов .refresh().

Рассмотрим в качестве примера обработчик формы авторизации, отправка которой происходит по AJAX:

$('body').on('form.auth', 'submit', function() {
        var query = $(this).serialize();
        var address = ... ;
        var post_callback = function() { ... } ;
        $.post(query, address, post_callback);
        return false;
});

В функции, записанной в переменную post_callback, проверяется ответ сервера, и в случае, если он говорит об успешной авторизации, еще одним запросом — с помощью .refresh() — обновляется AJAX-слой:3

var post_callback = function() {
    // ... (проверяем ответ) ...
    //
    // составляем набор блоков на обновление (в данном случае это легко,  
    // т.к. при авторизации нужно обновить все блоки без исключения)
    var patterns = [ '*' ];
    PERSONAL_AJAX_LAYER.refresh(patterns);
};

Другой пример — обработчик кнопки «купить». Нажатие на кнопку сопровождается отправкой на сервер запроса на добавление в корзину товара с данным id:

$('body').on('.buy', 'click', function() {
        var id = ... ; // id товара  
        var query = ... ;
        var address = ... ;
        var post_callback = function() { ... } ;
        $.post(query, address, post_callback);
        return false;
});

После проверки ответа нужно обновить блок с содержимым корзины. Сама кнопка в нормальном случае должна смениться надписью «товар в корзине», поэтому также нужно обновить контейнер этой кнопки, но не контейнеры остальных:4 очевидно, добавление в корзину товара никак не может повлиять на присутствие в ней других товаров.

var post_callback = function() {
    // ... (проверки) ...
   
    var patterns = [
        'cart',
        'buy*' + id // кнопку заменяем только у этого товара
    ];
    PERSONAL_AJAX_LAYER.refresh(patterns);
};

Callback-функции при обновлении слоя

Можно назначить дополнительные действия, которыми будет сопровождаться обновление блоков. Для этого их список, передаваемый методу .refresh(), нужно снабдить callback-функциями.

Предположим, при нажатии кнопки «купить» она должна заменяться на надпись «товар в корзине» не сразу, а плавно исчезая. Cоответствующая запись в списке блоков, подлежащих обновлению, из строки 'buy*' + id станет массивом, первым элементом которого будет вышеупомянутая строка, а вторым — callback-функция:

var post_callback = function() {
    // ... (проверки) ...
   
    var layer_callback = function( target, html ) {
        target.fadeOut( function() { target.html(html).fadeIn(); } );
    };
   
    var patterns = [
        'cart', // корзину описываем скалярной величиной, т.к. callback не нужен
        [ 'buy*' + id, layer_callback ] // элемент для кнопки теперь массив из двух элементов
    ];
    PERSONAL_AJAX_LAYER.refresh(patterns);
};

Callback-функция должна принимать два аргумента: первый — jQuery-объект элемента (в данном случае им выступает контейнер кнопки), второй — HTML-код для этого элемента, полученный с сервера.

Стандартное (без callback) обновление слоя выполняется функцией

function( target, html ) { target.html(html); }

Callback-функция применяется ко всем элементам, соответствующим переданному с ней в паре шаблону.

Серверная часть

Как уже было сказано ранее, содержимое блоков получается с сервера отдельным HTTP-запросом.

Этот запрос всегда делается методом POST и отправляется по одному и тому же пути, который указывается при создании объекта слоя в качестве параметра server.

Единственным параметром запроса является массив с именем, указанным в http_query_key при создании объекта. Он содержит информацию о членах AJAX-слоя, присутствовавших на странице на момент формирования запроса.

Для страницы, на которой имеются корзина, незаполненная форма авторизации и несколько товаров, POST-запрос будет выглядеть так:

Array(
    [personal_ajax] => Array(
        [0] => Array( [name] => cart:brief )
        [1] => Array( [name] => buy:23570  )
        [2] => Array( [name] => buy:13450  )
        [3] => Array( [name] => buy:60389  )
        ...
        [5] => Array( [name] => auth:brief,
                      [inputs] => Array( [email] =>  [password] => )
                    )
    )
)

Ключ name содержит имя блока и позволяет, таким образом, однозначно этот блок идентифицировать и сгенерировать для него HTML-код.

В inputs передаются текущие значения полей форм, если таковые имеются внутри блока. Их наличие позволяет при обновлении AJAX-слоя сохранять содержимое частично заполненных форм. (Если серверная часть написана на языке PHP, рекомендуется использовать специальное средство для генерации HTML-форм.)

В ответ нужно вернуть массив похожей структуры — добавить каждому элементу ключ html, в который записать HTML-код блоков:5

Array(
    [personal_ajax] => Array(
        [0] => Array( [name] => cart:brief, [html] => ... )
        [1] => Array( [name] => buy:23570,  [html] => <button ...>купить</button>  )
        [2] => Array( [name] => buy:13450,  [html] => <button ...>купить</button>  )
        [3] => Array( [name] => buy:60389,  [html] => товар в корзине  )
        ...
        [5] => Array( [name] => auth:brief, [html] => <form class="auth">...</form> )
    )
)

Ответ должен быть предусмотрен для всех возможных состояний каждого из блоков, входящих в состав слоя.

1. Необходима также библиотека jQuery.

2. На одной странице может существовать много AJAX-слоев, принадлежность к каждому из которых будет определяться наличием data-атрибута с определенным именем. При этом один и тот же блок может входить в состав нескольких слоев (имея data-атрибуты с соответствующими именами).

3. Включить в архитектуру AJAX-слоя отдельный запрос потребовалось, чтобы отделить логику работы его механизма от собственно обработки конкретного события.
Также отдельный запрос необходим, если содержимое блоков генерируется на основе cookie, т.к. последние приходят на сервер в обновленном виде только со следующим запросом.

4. Следует отметить, что использование строковой нотации вида 'buy*' + id может приводить к избыточному обновлению: например, в случае товара c id = 2 с сервера будет запрошен HTML-код не только для него, но и для товаров с любым id, содержащим двойку: 12, 23 и т.д.
Как правило, эта особенность не играет никакой роли. Но если этого все-таки требуется избежать, нужно использовать регулярное выражение — new RegExp('^buy:' + id + '$').

5. Если ответ сервера для некоторого блока вообще не содержит ключа html, при разборе ответа этот блок игнорируется и его содержимое в итоге не меняется.


© Все права на данную статью принадлежат порталу webew.ru. Перепечатка в интернет-изданиях разрешается только с указанием автора и прямой ссылки на оригинальную статью. Перепечатка в печатных изданиях допускается только с разрешения редакции.
Добавить комментарий
© 2008—2017 webew.ru, связаться: x собака webew.ru
Сайт использует Flede и соответствует стандартам WAI-WCAG 1.0 на уровне A.
Rambler's Top100