Javascript.RU

Интеграция AJAX в интерфейс

При создании компонент интерфейса часто нужен AJAX. Например - SELECT с подгружающимся списком, поле с автозаполнением, дерево с подгружающимися узлами и т.п.

Эта статья посвящена конкретному примеру удобной и универсальной реализации AJAX для компонента интерфейса. Она дает общие рамки, можно даже сказать "фреймворк" для создания таких виджетов.

Например, рассмотрим селект, загружающий данные с сервера. С точки зрения AJAX он почти ничем не отличается от подгружающегося дерева или таблицы.

Общий поток выполнения при AJAX-подгрузке данных, начиная от пустого селекта:

Список телодвижений при загрузке:

  1. Подготовить компонент для загрузки. Для нас - очистить селект
  2. Включить индикацию AJAX (loading...)
  3. Отправить асинхронный вызов
  4. Получить ответ сервера. Обработать ошибку, если она произошла.
  5. Загрузить данные в компонент
  6. Отключить индикацию AJAX

Для создания компоненты интерфейса используем функцию, которая принимает id узла SELECT и возвращает построенный компонент.

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

function ajaxSelect(id) {
    var element = document.getElementById(id)

    var onLoaded = function(data) {
        var i=0
        for(var key in data) {
            var label = data[key]
            element.options[i++] = new Option(label, key)
        }
    }
    
    var onLoadError = function(error) {
        var msg = "Ошибка "+error.errcode
        if (error.message) msg = msg + ' :'+error.message
        alert(msg)
    }
    
    var showLoading = function(on) {
        element.disabled = on
    }

    var onSuccess = function(data) {
        if (!data.errcode) {
            onLoaded(data)
            showLoading(false)        
        } else {
            showLoading(false)
            onLoadError(data)            
        }
    }
    
    
    var onAjaxError = function(xhr, status){
        showLoading(false)
        var errinfo = { errcode: status }
        if (xhr.status != 200) {
            // может быть статус 200, а ошибка
            // из-за некорректного JSON
            errinfo.message = xhr.statusText
        } else {
            errinfo.message = 'Некорректные данные с сервера'
        }
        onLoadError(errinfo)
    }

    
    return {
        load: function(url) {
            showLoading(true)

            while (element.firstChild) {
                element.removeChild(element.firstChild)
            }

            $.ajax({ // для краткости - jQuery
                url: url,
                dataType: "json",
                success: onSuccess,
                error: onAjaxError,
                cache: false
            })
        }
    }
}

Как это использовать:

  1. Создать в DOM/HTML элемент SELECT:
    <select id="ajax-select"></select>
    
  2. В javascript-коде инициализовать компонент:
    select = ajaxSelect('ajax-select')
    
  3. Например, по нажатию кнопки(onclick), вызвать загрузку:
    select.load('/ajax/ui/options.php')
    

А теперь - разберем все по порядку.

Использованный стиль описания яваскрипт-объекта описан, например, в учебнике по ООП и называется "фабрика объектов".

При стиле ООП "фабрика объектов" - приватные переменные обозначаются var, а доступные извне описываются как свойства возвращаемого return объекта.

Инициализация осуществляется непосредственно вызовом функции, без new:

select = ajaxSelect('...ID узла DOM...')

Так что функции onSuccess, onAjaxError, showLoading - приватные, а функция load - публичная.

Два первых метода onLoaded и onLoadError - основные. Любая AJAX-загрузка приводит к одному из них.

onLoaded(data)
Обрабатывает пришедшие данные. В нашем случае - заполняет ими опции селекта. Например, объект

{1:"Яблоко",2:"Дыня"}

превращается в опции селекта:

<option val="1">Яблоко</option>
<option val="2">Дыня</option>

Использованы стандартные методы работы с DOM (new Option).

onLoadError(error)
Обрабатывает любые ошибки. Объект error должен содержать свойство errcode - код ошибки, например "timeout" или "15541" и, дополнительно, может предоставлять более подробное описание ошибки в свойстве message.

В нашем примере - выдает форматированное сообщение об ошибке.

Метод индикации загрузки в примере просто включает-выключает селект.

Ничто не мешает добавить красивый анимированный значок загрузки. Желательно делать это через добавление CSS-класса к родительскому элемента select'а.

Получится что-то типа:

HTML-код для такой индикации:

<style>
.loading { 
    padding-left:20px;
    background: url(/ajax/ui/blue-loading.gif) left no-repeat;
}
</style>
<span class="loading">
<select disabled="disabled"></select>
</span>
showLoading(on)
Включает/выключает индикацию загрузки, в зависимости от значения on. Эти действия обычно похожи, поэтому удобно объединить их в один метод.

Как правило, ставит/убирает один CSS-класс.

Следующие два метода являются коллбэками для AJAX-запроса.

onSuccess(data)
Вызывается при успешном выполнении AJAX-запроса, получает прибывшие данные. Функция $.ajax автоматически интерпретирует их как JSON.

Чтобы сообщить о произошедшей ошибке, серверу достаточно передать, например, такой ответ:

{
    errcode: 500, 
    message: "Апдейт базы данных. Повторите запрос через 10 минут"
}

Обратите внимание на последовательность вызовов. Если все в порядке, то сначала вызывается обработка данных onLoaded - и только потом отключается индикация showLoading(false), чтобы посетитель увидел сразу заполненный селект.

Если произошла ошибка - удобнее сделать наоборот: сначала убрать индикацию загрузки, а затем вывести сообщение.

onAjaxError(xhr,status)
Коллбэк для ошибки при AJAX-запросе. Получает проблемный XmlHttpRequest и статус.

Вызов $.ajax автоматически интерпретирует ответ сервера как JSON - и если с этим проблемы, то хотя XmlHttpRequest выполнился успешно, но $.ajax вызывает коллбэк для ошибки и ставит status="parsererror".

Удобно то, что где бы ни произошла ошибка: во время выполнения XmlHttpRequest-запроса, или на сервере, или при разборе JSON - вызовется единый обработчик onLoadError.

Ну и, наконец, единственный публичный метод:

load(url)
Загружает данные в селект. Предварительно включает индикацию загрузки showLoading и удаляет всех детей, т.е очищает элемент.

Работающий селект можно посмотреть в действии:


Кнопки в этом примере инициализуются так:

$(document).ready(function() {
    var select = ajaxSelect('ajax-select');

    // урл, всегда выдающий пару разных фруктов
    document.getElementById('ajax-select-load-options').onclick =
        function() { select.load('/ajax/ui/options.php') }
    
    // урл, всегда выдающий ошибку
    document.getElementById('ajax-select-load-error').onclick =
        function() { select.load('/ajax/ui/error.php') }
    
});

Разумеется, никто не мешает передавать более сложный URL, добавлять туда id и другую полезную информацию.

Вы:

  • Познакомились с потоком выполнения при AJAX-загрузке данных.
  • Посмотрели, как его реализовать с унифицированной обработкой ошибок.
  • Порадовались, как прост и элегантен грамотный javascript-код.

Автор: ShUtnik, дата: 4 мая, 2009 - 01:22
#permalink

спасибо хорошая статья


Автор: Гость (не зарегистрирован), дата: 13 мая, 2009 - 03:37
#permalink

спасибо


Автор: Гость (не зарегистрирован), дата: 25 мая, 2009 - 19:21
#permalink

Сюда бы еще архивчик в конце со всеми исходниками, и было бы вообще просто супер.


Автор: Гость (не зарегистрирован), дата: 1 октября, 2009 - 13:18
#permalink

А как сделать options.php что бы он возвращал массив в стиле js?


Автор: Илья Кантор, дата: 2 октября, 2009 - 14:45
#permalink

Можно использовать кодировщик в JSON. При этом для windows-1251 лучше кодировать самому, а в UTF-8 можно из PECL взять.


Автор: Гость (не зарегистрирован), дата: 19 октября, 2009 - 09:09
#permalink

Данные возвращаются в непонятной кодировке


Автор: Гость (не зарегистрирован), дата: 23 октября, 2009 - 11:00
#permalink

Для работы с аяксом необходимо использовать только UTF, к сожалению. Сам от этого страдаю, все сайты в 1251 сделаны.


Автор: Гость (не зарегистрирован), дата: 28 июня, 2010 - 15:54
#permalink

Ну так iconv есть для этого. Сам им все строки перегоняю между браузером и БД.


Автор: Гость (не зарегистрирован), дата: 9 октября, 2012 - 17:36
#permalink

Мухахахаха


Автор: Гость (не зарегистрирован), дата: 29 ноября, 2009 - 16:28
#permalink

Можно поподробнее насчет добавления CSS-класса к родительскому элементу select'а? Как сделать его неактивным в начале работы, когда кнопка еще не нажата?


Автор: katzo (не зарегистрирован), дата: 3 февраля, 2010 - 02:37
#permalink

Не пойму, почему не работает тогда код:

<script src="http://javascript.ru/ajax/ui/ajaxselect.js"></script><select  style="width:100px" id="ajax-select"></select>
<p><input type="button" id="ajax-select-load-options" value="Загрузить опции"/><br />
<input type="button" id="ajax-select-load-error" value="Загрузить ошибку"/></p>

Как заставить это работать?!


Автор: Гарик (не зарегистрирован), дата: 12 марта, 2010 - 14:31
#permalink

Почему не работает? - очень даже работает. Проверьте в файле ajaxselect.js в строках:

$(document).ready(function() {
    var select = ajaxSelect('ajax-select');
    document.getElementById('ajax-select-load-options').onclick =
        function() { select.load('options.php') }
    
    document.getElementById('ajax-select-load-error').onclick =
        function() { select.load('error.php') }

пути к файлам options.php и error.php


Автор: Гарик (не зарегистрирован), дата: 12 марта, 2010 - 14:37
#permalink

У меня вот другая проблема. Не могу заставить работать этот пример в модальном окне facebox. Илья, подскажите пожалуйста путь решения или в каком направлении копать?


Автор: Яростный Меч, дата: 14 августа, 2010 - 20:41
#permalink

Недавно делал загрузку опшенов с сервера. Одному комбобоксу по воле судьбы полагалось 24000 оных. Вставка через дом происходила немыслимо долго в IE8 (после минуты ждать не стал). В итоге пожертвовал траффиком и идеологическими соображениями, стал грузить готовый html (строку с опшенами) и использовать вставку через innerHTML, это отрабатывает почти мгновенно.


Автор: Гость (не зарегистрирован), дата: 17 августа, 2010 - 09:38
#permalink

А как реализовать подгрузку данных сразу а не по нажатию на кнопку ?


Автор: Яростный Меч, дата: 18 августа, 2010 - 13:48
#permalink

$(function(){
/* здесь запускаем подгрузку */
});

или window.onload/DOMContentLoaded


Автор: Гость (не зарегистрирован), дата: 17 февраля, 2011 - 17:51
#permalink

Вот так работает, правда тока в IE6 =))

options.php :

<?php

$result='{1:"Дыня",2:"Яблоко"}';

header("Content-type: text/html; charset=windows-1251");

echo $result;

?>


Автор: Гость (не зарегистрирован), дата: 17 февраля, 2011 - 18:06
#permalink

под OPERA и FF

нехочет заводиться пишед данные не нравятся с сервера


Автор: Ёрка (не зарегистрирован), дата: 2 марта, 2012 - 13:43
#permalink

Выдаёт ошибку Uncaught ReferenceError: $ is not defined, строка 55 в первом листинге.

Что вообще там обозначает коммент "для краткости - jQuery"?

---
Заранее спасибо!


Автор: Юрий123456 (не зарегистрирован), дата: 10 апреля, 2012 - 15:39
#permalink

библиотеку jQuery подключи!
Да и так почитай о ней, полезно
jQuery.com


Автор: Гость (не зарегистрирован), дата: 12 мая, 2012 - 23:45
#permalink

ребят, у меня проблема. Мне нужно как то установить соединение клиента с сервером что получать текстовые обновления на экран для первого. Первое что пришло в голову это сделать так чтобы клиент каждые 5 сек запрашивал у сервака информацию об обновлении, отсюда трафик лишний. Вопрос, как сделать так чтобы при появлении обновления сервак сам отправил клиенту данные? это вопрос синхронизации(асинхронизации), или какие то широковещательные посылки должны быть, я хз. Кто понял о чём я ,отзовитесь плиз!


Автор: Гость (не зарегистрирован), дата: 26 марта, 2015 - 11:20
#permalink

Доброго времени суток

$("#los_save").click(function ()
{
/*
var data1 = $('#faz1').val();
var data2 = $('#faz2').val();
var data3 = $('#faz3').val();
var data4 = $('#faz4').val();
......
var data15 = $('#faz15').val();
*/

var fl=0;
var fl2=15;
for (var i = fl; i <= fl2; i++)
{
var data[i] = $("#faz[i]").val(); /// ???
var arr = ["data[i]", i, true]
}
$.ajax({type: "POST",url: "proba.php", data:"arr", });

});
'#faz1' - id input. Подскажите как правильно написать $("#faz[i]").val() ?


Автор: DeMItt (не зарегистрирован), дата: 5 апреля, 2015 - 18:31
#permalink
$("#"+faz[i]).val();

Автор: DeMitt (не зарегистрирован), дата: 5 апреля, 2015 - 18:34
#permalink

Тьфу. Туплю.Вот:
$("#faz"+i).val();


Автор: DeMitt (не зарегистрирован), дата: 5 апреля, 2015 - 18:36
#permalink

И у тебя нет идентификатора "faz0", поэтому начинай i не с нуля, а с единицы.


Отправить комментарий

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
4 + 7 =
Введите результат. Например, для 1+3, введите 4.
 
Текущий раздел
Поиск по сайту
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Последние комментарии
Последние темы на форуме
Forum