Формы и меню
7.3.3. Отправка форм
XUL не связывает элементы форм с целевым URL так, как это делает HTML, но все же цель заполнения формы - отправить куда-то информацию. В Mozilla возможности хранения данных так же универсальны и разнообразны, как и в любых других средах программирования. Это не очень удобно, если вы хотите быстро создать работающий прототип. Mozilla предоставляет два варианта эффективной отправки данных XUL-формы на web-сервер.
7.3.3.1. Отправка данных XUL-форм с помощью HTML
Первый метод отправки данных XUL-формы, очень быстрый и не самый аккуратный, подразумевает использование пространств имен XML. Можно создать документ, который начинается с XUL, но включает все функции HTML. В листинге 7.1 показан такой документ.
<?xml version="1.0"?>
<!DOCTYPE window>
<window xmlns="http://www.mozilla.org/keymaster/
gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
<vbox>
<script>
function copy() {
var getID = document.getElementByID;
getID("h1").value = getID("x1").value;
getID("h2").value = getID("x2").value;
return true;
}
</script>
<html:form action="test.cgi" method="GET"
enctype="application/x-www-form-urlencoded">
<html:input id="h1" type="hidden"/>
<html:input id="h2" type="hidden"/>
<radiogroup>
<button id="x1" label="Кнопка 1" type="radio"/>
<button id="x2" label="Кнопка 2" type="radio"/>
</radiogroup>
<html:input type="submit" onsubmit="return copy();">
</html:form>
</vbox>
</window>
Листинг
7.1.
Смесь HTML- и XUL-форм
Этот документ отображает форму, состоящую из двух XUL-кнопок и одной XHTML-кнопки для отправки данных. В обычном XHTML нельзя поместить кнопки в группу переключателей, но здесь это допустимо, так как Mozilla поддерживает сочетание XUL и HTML. XHTML-элементы формы связываются с процессом отправки данных формы, но XUL-элементы - нет. Простая функция на JavaScript копирует необходимые данные в скрытые поля перед отправкой информации.
Можно создать действительный документ из XHTML+XUL, не прибегая к пространствам имен xmlns. Для этого нужно начать с "чистого" XHTML-документа (или XUL-документа) и добавить DTD-сущности для XUL- приложения (или XHTML-приложения) в объявление <!DOCTYPE>. Такой документ не использует особый механизм xmlns, с помощью которого Mozilla определяет тип документа. Это значит, что для таких дополнительных тегов не будет использоваться никакая дополнительная обработка (поддержка). Это отсутствие определения типов - причина того, что добавление тега <A> в XUL-документ не приводит к созданию XHTML-ссылки.
7.3.3.2. Объект XMLHttpRequest
Во второй методике отправки данных XUL-формы используется объект XMLHttpRequest. Это доступный из скриптов AOM-объект, применимый во всех XML-документах, как, например, объект Image в HTML-документах. Он позволяет отправлять HTTP-запросы напрямую из JavaScript. Ответ сервера на такой запрос не замещает текущий отображаемый документ. Такой ответный документ просто читается как одна большая строка. Объект XMLHttpRequest основывается на следующем XPCOM-компоненте:
@mozilla.org/xmlextras/xmlhttprequest;1
Этот компонент реализует XPCOM-интерфейсы nsIXMLHttpRequest и nsIJSXMLHttpRequest, которые хорошо объясняются в файлах определений. Эти интерфейсы позволяют отправлять HTTP-запросы синхронно и асинхронно. Синхронная отправка означает, что скрипт приостанавливает свою работу, пока не будет получен полный ответ. Асинхронная отправка - аналог самонаводящегося снаряда, только этот запрос можно отслеживать, а конечный результат - забрать. В листинге 7.2 показано, как работают синхронные запросы.
var req = new XMLHttpRequest();
// Запрос
var res = null;
// Ответ
var params = encodeURI("param1=value1;param2=value2");
// -- GET-запрос
req.open("GET", "test.cgi" + "?" + params);
req.send("");
if ( req.status / 100 == 2 )
// Ответ HTTP 2xx?
res = req.responseText;
// -- POST-запрос
req.open("POST", "test.cgi");
req.send(params);
if ( req.status / 100 == 2 )
// Ответ HTTP 2xx?
res = req.responseText;
Листинг
7.2.
Примеры синхронных запросов XMLHttpRequest
Функция encodeURI() - аналог escape() для ECMAScript ; поддерживаются обе. Второй аргумент open() - любой корректный URL. Так как send() не возвращает ничего, пока пара запрос-ответ не будет полной, программист должен предоставить пользователю какой-нибудь индикатор вроде "Ожидается..." сразу перед вызовом send(), чтобы пользователь знал, что приложение продолжает работу, а не просто "зависло".
Асинхронная отправка данных форм полезна, когда нужно выполнить несколько HTTP-запросов. Более эффективно отправить все запросы за раз, а затем время от времени проверять их состояние. Простейший способ выполнить асинхронную отправку - поместить ее в функцию и запланировать с помощью setTimeout(). В листинге 7.3 показан более формальный и структурированный подход с использованием интерфейса nsIXMLHttpRequest.
var req = new XMLHttpRequest(); // Запрос var res = null;
// Ответ var url = "test.cgi?text1=value1";
// Часть, специфичная для асинхронных запросов
function finished() {
res = req.responseText;
}
function inprogress() {
if ( req.readyState != req.COMPLETED ) {
res = "Waiting ...";
setTimeout(inprogress, 100);
}
}
req.COMPLETED = 4;
// из интерфейса
req.onload = finished;
// -- GET-запрос (POST аналогичен)
req.open("GET", url, false);
// false == асинхронный req.send();
// следующая инструкция выполняется немедленно
setTimeout(inprogress,100);
Листинг
7.3.
Пример асинхронного запроса XMLHttpRequest
В этом примере метод send() возвращает ? почти сразу, оставляя HTTP-запрос в обработке. Функция finished() создана как обработчик событий, который запускается, когда ответ наконец-то получен полностью. Между этими двумя точками во времени используется setTimeout() для простой регулярной проверки состояния. При использовании таких асинхронных запросов пользователю могут не требоваться уведомления о состоянии. Тем не менее, программист должен позаботиться о том, чтобы последующие действия пользователя не повлияли на обработку ответа, когда он появится. Например, пользователи, покупающие акции, не должны иметь возможность опустошить свои банковские счета во время процесса покупки акций.