|
При создании компонент интерфейса часто нужен AJAX. Например - SELECT с подгружающимся списком, поле с автозаполнением, дерево с подгружающимися узлами и т.п.
Эта статья посвящена конкретному примеру удобной и универсальной реализации AJAX для компонента интерфейса. Она дает общие рамки, можно даже сказать "фреймворк" для создания таких виджетов.
Например, рассмотрим селект, загружающий данные с сервера. С точки зрения AJAX он почти ничем не отличается от подгружающегося дерева или таблицы.
Общий поток выполнения при AJAX-подгрузке данных, начиная от пустого селекта:
Список телодвижений при загрузке:
- Подготовить компонент для загрузки. Для нас - очистить селект
- Включить индикацию AJAX (loading...)
- Отправить асинхронный вызов
- Получить ответ сервера. Обработать ошибку, если она произошла.
- Загрузить данные в компонент
- Отключить индикацию 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
})
}
}
}
Как это использовать:
- Создать в DOM/HTML элемент SELECT:
<select id="ajax-select"></select>
- В javascript-коде инициализовать компонент:
select = ajaxSelect('ajax-select')
- Например, по нажатию кнопки(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?
Автор: Гость (не зарегистрирован), дата: 19 октября, 2009 - 09:09
#permalinkДанные возвращаются в непонятной кодировке
Автор: Гость (не зарегистрирован), дата: 29 ноября, 2009 - 16:28
#permalinkМожно поподробнее насчет добавления CSS-класса к родительскому элементу select'а? Как сделать его неактивным в начале работы, когда кнопка еще не нажата?
Автор: katzo (не зарегистрирован), дата: 3 февраля, 2010 - 02:37
#permalinkНе пойму, почему не работает тогда код:
Как заставить это работать?!
Автор: Гарик (не зарегистрирован), дата: 12 марта, 2010 - 14:37
#permalinkУ меня вот другая проблема. Не могу заставить работать этот пример в модальном окне facebox. Илья, подскажите пожалуйста путь решения или в каком направлении копать?
Автор: Яростный Меч, дата: 14 августа, 2010 - 20:41
#permalinkНедавно делал загрузку опшенов с сервера. Одному комбобоксу по воле судьбы полагалось 24000 оных. Вставка через дом происходила немыслимо долго в IE8 (после минуты ждать не стал). В итоге пожертвовал траффиком и идеологическими соображениями, стал грузить готовый html (строку с опшенами) и использовать вставку через innerHTML, это отрабатывает почти мгновенно.
Автор: Гость (не зарегистрирован), дата: 17 августа, 2010 - 09:38
#permalinkА как реализовать подгрузку данных сразу а не по нажатию на кнопку ?
Автор: Гость (не зарегистрирован), дата: 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"?
---
Заранее спасибо!
Автор: Гость (не зарегистрирован), дата: 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() ?
Автор: Гость (не зарегистрирован), дата: 17 февраля, 2022 - 12:15
#permalinkНу, с этого и следовало начинать!
Автор: Гость (не зарегистрирован), дата: 20 января, 2023 - 15:46
#permalinkСпасибо за крутую идею
Автор: Гость (не зарегистрирован), дата: 20 января, 2023 - 15:48
#permalinkЧитать стоит, однозначно рекомендую. Отличная работа
Автор: Alice12 (не зарегистрирован), дата: 6 августа, 2024 - 06:25
#permalinkAJAX (Asynchronous JavaScript and XML) позволяет отправлять запросы к серверу и получать данные асинхронно, fnf что делает веб-страницы более интерактивными.