Формы и меню
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() для простой регулярной проверки состояния. При использовании таких асинхронных запросов пользователю могут не требоваться уведомления о состоянии. Тем не менее, программист должен позаботиться о том, чтобы последующие действия пользователя не повлияли на обработку ответа, когда он появится. Например, пользователи, покупающие акции, не должны иметь возможность опустошить свои банковские счета во время процесса покупки акций.