K4:Работа с AJAX запросами
From In-Portal Developers Guide
| ||
---|---|---|
Статьи в этой категории | ||
|
AJAX
(asynchronous JavaScript
and XML
) - подход к Web-программированию, позволяющий браузеру обмениваться информацией с сервером без перезагрузки страницы. AJAX
не является самостоятельным языком программирования или технологией, это всего лишь концепция, объединяющая в себе использование нескольких языков и технологий.
Общая AJAX
-модель заключается в следующем:
- имеется web-страница, которая при помощи специального
JavaScript
-объекта посылает запрос на сервер. Запрос обычно содержит какие-либо данные, динамические полученные от клиента - например, значения заполненных на форме полей; - сервер получает запрос, обрабатывает его своими средствами, и возвращает браузеру результат. Для сервера полученный запрос ничем не отличается от запроса, который браузер посылает при загрузке любого
URL
. Соответственно, обработка также ничем не отличается; - результат, сгенерированный сервером, попадает в специальный
JavaScript
-объект, а также вызывается событие, которое инициирует обработку данного объекта. Чаще всего затем полученные данные используются для отображения каких-либо изменений на странице с помощью технологииDHTML
(изменения исходногоHTML
с помощьюJavaScript
'a).
Contents |
Описание AJAX подхода (в K4)
В течение последних нескольких лет AJAX
стал активно использоваться, поскольку появилось достаточное количество задач, где данный подход является оптимальным. Для этого был написан специальный JavaScript
-класс - Request
. Данная статья описывает работу с AJAX при помощи данного класса. Также использование этого класса настоятельно рекомендуется при любой работе с AJAX
, т.к. в нём уже учтены многие вещи, на самостоятельное решение которых может быть потрачено значительное время.
Для того, чтобы использовать класс Request
на Front-End
, в шаблоне должен быть подключен файл ajax.js
. В административной консоли этот файл подключается автоматически в шаблоне "header.tpl
". Чтобы сделать простой запрос, нужно использовать метод makeRequest
класса Request
:
var p_busyReq = false; Request.makeRequest(p_url, p_busyReq, p_progId, p_successCallBack, p_errorCallBack, p_pass, p_object);
Данный метод принимает следующие параметры:
название | описание |
---|---|
p_url (string) | URL-адрес, к которому производится запрос. |
p_busyReq (bool) | Индикатор "занятости" AJAX объекта. При значении true запрос не выполнится.
|
p_progId (string) | ID HTML-элемента (обычно это div элемент), в котором показывать процесс загрузки данных через AJAX. После успешного процесса загрузки данных в этот элемент автоматически будет помещён весь полученный результат.
|
p_successCallBack (function) | Функция обратного вызова (callback), которая будет вызвана при успешном завершении запроса. |
p_errorCallBack (function) | Функция обратного вызова (callback), которая будет вызвана в случае ошибки. |
p_pass (mixed) | Параметр(-ы), который может использоваться для передачи дополнительной информации, нужной для обработки полученного AJAX-ответа. |
p_object (object) | Если функция обратного вызова является методом объекта, то нужно передавать этот объект в качестве данного параметра. |
При получении ответа (успешного или нет) вызывается соответствующая функция обратного вызова. При каждом вызове в неё передаётся 3 параметра:
название | описание |
---|---|
req (object) | Объект, содержащий в себе результат AJAX-запроса. |
p_pass (mixed) | Значение из аналогичного параметра, переданного в метод makeRequest .
|
p_object (object) | Значение из аналогичного параметра, переданного в метод makeRequest .
|
Ниже приведены примеры написания функций обратного вызова, передаваемых методу makeRequest
:
function successCallBack (req, p_pass, p_object) { var result_text = req.responseText; // полученный текст var result_xml = req.responseXML; // полученный XML // работа с результатом запроса } function errorCallBack (req, p_pass, p_object) { var error_code = req.status; // обработка ошибки }
XML-объект в AJAX-ответе доступен только в случае, когда в ответе был послан соответствующий "Content-Type
" заголовок.
Использование
На форме имеется элемент ограниченного выбора (dropdown), использующийся для выбора пользователя системы (например, если это форма ввода какого-либо документа, то это может быть пользователь, который должен будет этот документ обработать). В элементе отображается имя, фамилия и адрес электронной почты, однако нужно видеть все данные выбранного пользователя, чтобы быть уверенным, что выбран правильной человек.
Поиск решения
- Можно значения всех полей пользователя попробовать вывести в
dropdown
, но удобство такого решения оставляет желать лучшего. - Можно попробовать загрузить все данные в
JavaScript
массив и показывать соответствующие значения вread-only
полях при смене выбранного значения. Этот вариант намного лучше предыдущего, однако он (как в принципе и предыдущий) обладает существенным недостатком - загружается большое количество данных, большинство из которых, скорее всего, использовано не будет, но тем не менее, при большом количестве данных может повлиять на время загрузки страницы и на работоспособность браузера. - Такой же вариант, как и предыдущий, но с одним отличием: значения полей конкретного пользователя будут загружаться только при смене значения, выбранного в
dropdown
. Для этой цели отлично подойдётAJAX
. Ввиду отсутствия недостатков предыдущих вариантов данный подход является оптимальным.
Решение
В первую очередь нужно создать объект для работы с данными пользователя:
function UserInfoManager() { this.userSelect = document.getElementById('<inp2:document_InputName field="ResponsibleUserId" />'); this.busy = false; addEvent(this.userSelect, 'change', this.getUserInfo); }
Хотя, в принципе, создание объекта не является необходимым, но это является более правильным подходом и настоятельно рекомендуется при выполнении задач любого уровня сложности. Также в данном случае создаётся именно объект, а не класс, поскольку здесь он требуется для выполнения одной единственной конкретной задачи. В случае, если схожая функциональность требуется для решения нескольких задач, рекомендуется создавать класс и работать в каждом случае с его экземплярами.
Для упрощения работы добавлено свойство userSelect
, а также выполнение метода getUserInfo
назначено на событие change
соответствующему объекту. Затем требуется добавить метод, который будет осуществлять запрос:
UserInfoManager.getUserInfo = function() { var user_id = this.userSelect.value; if (user_id) { var url = this.getRequestUrl(user_id); Request.makeRequest(url, this.busy, null, this.receiveUserInfo, this.processRequestError, '', this); } } UserInfoManager.getRequestUrl = function(user_id) { var url = '<inp2:m_Link pass="all,document" doc_event="OnGetUserInfo" UserId="#USER_ID#" />'; return url.replace('#USER_ID#', user_id); }
В методе getUserInfo
в качестве обработчиков ответа установлены методы receiveUserInfo
и processRequestError
, а также передан текущий объект. Также добавлен метод getRequestUrl
, который подменяет шаблон #USER_ID#
на ID
пользователя, для которого производиться запрос.
В классе DocumentEventHandler
, который является обработчиком событий для префикса "document
" нужно добавить метод для формирования и отсылки ответа с сервера:
function OnGetUserInfo(&$event) { // отсылка заголовка с Content-Type text/xml $this->Application->XMLHeader('1.0'); $user =& $this->Application->recallObject('u.-getinfo', 'u', Array ('skip_autoload' => true)); /* @var $user UsersItem */ $user_id = $this->Application->GetVar('UserId'); $user->Load($user_id); $result = ''; foreach ($user->Fields as $field_name => $field_options) { $result .= '<field name="' . $field_name . '">' . $user->GetField($field_name) . '</field>'; } echo '<fields>' . $result . '</fields>'; $event->status = erSTOP; }
Следует обратить внимание на то, что результат выводится с помощью функции echo
и то, что событию присваивается статус erSTOP
. Данный статус был специально разработан для событий, вызываемых с помощью AJAX
'a и означает, что скрипт после выполнения данного события прекратит работу, а также, в случае включенного режима отладки, предотвратит вывод сообщений отладчика (что могло бы "поломать" XML
или привести к появлению ненужных данных в результате запроса).
Таким образом, браузеру вернётся примерно следующий XML
:
<?xml version="1.0" encoding="utf-8"?> <fields> <field name="PortalUserId">1</field> <field name="Login">intechnic</field> <field name="Email">sergeyg@intechnic.lv</field> <field name="FirstName">Сергей</field> <field name="LastName">Гриб</field> <field name="Phone">12345678</field> <field name="Fax">12345679</field> ... </fields>
Теперь требуются обработка возвращённых данных. Для этого у используемого JavaScript
объекта пишется соответствующий метод:
UserInfoManager.receiveUserInfo = function(req, p_pass, p_object) { var user_info_xml = req.responseXML; var fields_arr = user_info_xml.getElementsByTagName('FIELD'); for(var i = 0; i < fields_arr.length; i++) { var field_name = fields_arr[i].getAttribute('name'); var control = p_object.getControl('User' + field_name); if (control) { control.innerHTML = fields_arr[i].innerText; } } } UserInfoManager.getControl = function(field_name) { var field_mask = '<inp2:document_InputName field="#FIELD#" />'; return document.getElementById( field_mask.replace('#FIELD#', field_name) ); }
Следует обратить внимание на то, что обращение к объекту UserInfoManager
внутри функции обратного вызова идёт через объект, переданный в параметре p_object
. Это требуется из-за того, что с момента назначения метода объекта в качестве функции обратного вызова в нём уже не возможно использовать ключевое слово "this
".
Описание AJAX подхода (в принципе)
Для работы с AJAX
-запросами используется объект XMLHttpRequest
. В Internet Explorer
'e его можно получить как ActiveX
-объект, в остальных браузерах (Firefox
, Opera
и других) он является встроенным JavaScript
объектом. Поэтому для получения объекта обычно используется функция, внутри которой объект получается в зависимости от браузера, например:
function getXMLHttpObject() { try { // Firefox, Opera 8.0+, Safari return new XMLHttpRequest(); } catch (e) { // Internet Explorer try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { alert("Your browser does not support AJAX!"); return false; } } } }
Для того чтобы послать запрос, используются методы open
и send
:
httpRequest = new getXMLHttpObject(); httpRequest.open('GET', 'http://www.intechnic.lv/get_result.php?some_param=some_value', true); httpRequest.send(null);
Методу open
передаются 3 параметра:
- Метод протокола
HTTP
, который будет использован для запроса, обычно этоGET
. -
URL
, на который запрос будет послан. - Указывает на то, что запрос должен выполняться синхронно со страницей, его вызвавшей.
Третий параметр является ключевым при использовании AJAX
'a - при значении true
выполнение Javascript
'a продолжится сразу после посылки запроса, не дожидаясь ответа, другими словами, посылается асинхронный запрос. При значении false
выполнение кода не будет продолжено до тех пор, пока не будет получен ответ от сервера, т.е. будет отослан синхронный запрос.
Для того, чтобы узнать текущий статус выполнения запроса, используется свойство readyState
объекта httpRequest
. Его значения расшифровываются следующим образом:
-
0
- не загружался; -
1
- загружается; -
2
- загрузился; -
3
- обмен данными; -
4
- готово.
Чтобы узнать результат выполнения AJAX-запроса, нужно проверить значение свойства status
. В нём хранится стандартный HTTP response code
(например, 404 - page not found, 403 - forbidden, 500 - internal server error
и другие). А чтобы "поймать" момент, когда статус будет изменён, имеется свойство onreadystatechange
, в которое нужно присвоить функцию обратного вызова, которая будет вызвана в момент изменения статуса:
httpRequest.onreadystatechange = function() { try { if (httpRequest.readyState == 4) { if (httpRequest.status == 200) { alert(httpRequest.responseText); } else { alert('Request failed: error ' + httpRequest.status + ' encountered!'); } } } }
Как видно из этого запроса, для получения текста результата используется свойство responseText
. Таким образом, на экран выведется текст "SOME_VALUE", если файл get_result.php
будет содержать следующий код:
<?php echo strtoupper($_GET['some_param']);