
Ajax в действии
.pdfГлава 12. "Живой" поиск с использованием XSLT 491
?>
<entry id='<?=$rayrow['id']?>001'>
<company><?=$myrow['companyNarae']?></company> <contactx?=$myrow['contactName']?></contact> <countryX?=$myrow['country1 ]?></country>
<phone><?=$myrow [ ' phone' ] ?x/phone> </entry>
<?
}while ($myrow - mysql_fetch_array{$result)); }else{
?>
<entry id='0011 >
//@ Показать пустой набор данных
<company>No Results</company> <contact>N/A</contact> <country>N/A</country> <phone>N/A</phone>
</entry> <? } ?> </phonebook>
Чтобы данная динамическая генерация XML-документа удалась, тип MIME документа необходимо установить равным text/xml О; если пропустить этот этап, XSLT-преобразование может не произойти (особенно в браузерах Mozilla и Firefox).
Требуемые нам данные хранятся в таблице базы данных, и нам необходимо просто выбрать нужные записи. В данном случае, для того чтобы максимально упростить работу, мы для непосредственного общения с базой данных используем встроенные функции MySQL языка РНР. Мы соединяемся с базой данных ajax, запущенной на локальном сервере базы данных, используя имя пользователя ajax и пароль action ©. После этого создается строка SQL-запроса; для заполнения оператора WHERE применяется параметр request, переданный в клиентском коде ©.
Для более надежной реализации серверной части приложения рекомендуется не связываться напрямую с базой данных, как в приведенном коде, а использовать структуру, подобную DB_DataObject (Pear) (см. главу 3). Впрочем, текущая реализация очень проста, и читатели, желающие самостоятельно протестировать рассматриваемый пример, могут ее легко настроить.
Получив множество результатов, мы проверяем наличие в нем данных О, а затем либо последовательно проходим по нему © для создания записей телефонной книги, либо выдаем сообщение "No Results" 0.
12.3.2. Создание документа XSLT
Используя XSLT, наш XML-файл с помощью пары строк кода можно преобразовать в красивую таблицу HTML. XSLT-документ разрешает сопоставление с шаблоном, если оно необходимо для отображения данных в любом требуемом формате. При сопоставлении с шаблоном для отображения данных применяется структура-шаблон. При этом мы проходим по узлам дере-
492 Часть IV. Ajax в примерах
ва источника, используя XSLT. XSLT-доку мент принимает структурированный XML-файл и преобразует его в формат, который удобен для просмотра, обновления и изменения. В нашем случае XSLT-доку мент определяется статически.
Структура XSLT
XSLT-преобразование содержит правила для перевода исходного дерева в конечное. Весь XSLT-процесс заключается в сопоставлении со структуройшаблоном. Когда элементы исходного дерева соответствуют заданной структуре, согласно шаблону документа создается конечное дерево.
Структура конечного дерева не обязательно должна быть связана со структурой исходного. Следовательно, исходный XML-файл можно преобразовать в любой требуемый формат. Использовать только табличное представление набора данных не обязательно.
XSLT-преобразование называется таблицей стилей, поскольку оно определяет стилевое оформление конечного дерева. Таблица стилей содержит правила шаблона, состоящие из двух частей. Первая часть — это структурашаблон, с которой сравниваются узлы исходного дерева. Обнаружив соответствие, XSLT-процессор задействует вторую часть — шаблон, содержащий дескрипторы для построения исходного дерева.
Создание XSLT-документа
Сформировать XSLT-преобразование для данного проекта сравнительно просто. Поскольку мы собираемся получить таблицу, никакое необычное сопоставление с шаблоном не потребуется; мы просто последовательно пройдем по всем узлам-элементам исходного дерева. Ниже мы разработаем шаблон, формирующий таблицу HTML с четырьмя столбцами. Соответствующий XSLTфайл для данного проекта показан в листинге 12.6.
i Листинг 12.6. XSLT-файл
<?xml version="1.0" encoding=>"ISO-8859-l"?>
//О Установить версию XML и кодировку <xsl:stylesheet version="l.О" xmlns:xsl=
//© Задать пространство имен XSLT "http://www.w3.org/1999/XSL/Transform">
//© Установить правила шаблона <xsl:template match="/">
//О Добавить элемент table
<table id="tablel">
// © Создать строку заголовка <tr>
<th align="left">Company</th> <th align="left">Contact</th> <th align="left">Country</th> <th align="left">Phone</th>
</tr>
Глава 12 "Живой" поиск с использованием XSLT 493
// 0 Последовательно пройти по элементам телефонной книги <xsl:for-each
select="phonebook/entry"> // © Отформатировать выходные данные
<tr>
<tdxxsl:value-of select="company"/></td> <td><xsl:value-of select="contact"/></td> <td><xsl:value-of select="country"/></td> <td><xsl:value-of select="phone"/></td>
</tr>
</xsl:for-each> </table>
</xsl:template>
</xsl:stylesheet>
При создании XSLT-преобразования необходимо указать кодировку и версию XML О, а также задать пространство имен XSLT ©. Пространство имен определяет правила и спецификации, которым должен соответствовать документ. Элементы в пространстве имен XML распознаются в исходном документе, но не распознаются в документе результатов. Для определения всех наших элементов в пространстве имен XSLT применяется префикс xsl. Далее молено установить правило шаблона — искать структуру / ©, которая соответствует всему документу.
Теперь можно начинать создание шаблона таблицы, в которой отображаются наши результаты. Мы добавляем дескриптор table О, сопоставляющий с таблицей идентификатор. После этого вводится строка заголовка таблицы ©, вмещающая имена столбцов, указывающих пользователю, какая информация содержится в таблице.
Последовательно проходя по множеству узлов исходного дерева, мы получаем остальные строки таблицы. В данном случае используется цикл for-each ©, в процессе обработки записей выдающий узлы, расположенные в phonebook/entry.
Поскольку мы последовательно проходим по дереву документа, необходимо выбрать значения столбцов. Чтобы выбрать значения из узлов, используется оператор value-of в, извлекающий значение элемента XML и добавляющий его в выходной поток преобразования. Чтобы задать элемент XML, текст которого мы желаем извлечь, используем с именем элемента атрибут select. Сформировав XSLT-фаЙл и создав код для динамической генерации документа XML, можно завершить создание JavaScript-кода и посмотреть, как объединение XSLT-преобразования со структурированным XML-файлом позволяет получить удобную для просмотра таблицу.
На следующем этапе мы снова возвращаемся на сторону клиента, извлекающего файлы, созданные только что с помощью НТТР-отклика.

494 Часть IV. Ajax в примерах
12.4. Объединение документов XSL и XML
Возвращаясь на сторону клиента, мы сталкиваемся с задачей объединения полученных с сервера документов XSL и XML. При использовании XSLT-преобразования следует помнить, что браузеры по-разному объединяют документы указанных типов. Следовательно, вначале необходимо проверить, какой метод поддерживает браузер, чтобы загрузить и объединить два документа.
Как и ранее, мы используем объект ContentLoader (см. главу 3). Данный объект содержится во внешнем JavaScript-файле net . js . Этот файл определяет, как отправлять информацию на сервер, скрывая все отличия работы браузеров за удобным интерфейсным объектом.
<script type="text/javascript" src="net.js"X/script>
Теперь можно начинать процесс получения файлов с сервера и объединения их на стороне клиента. В листинге 12.7 приведена функция LoadXMLXSLTDoc (), вызываемая из функции GrabNumber(), представленной в листинге 12.2. Функция GrabNumber() передает значения URL, генерирующего XML-данные, XSL-файл и идентификатор элемента, в который должны выводиться данные. Имея три указанных значения, мы можем загрузить два документа и объединить их после завершения загрузки. Как показано в листинге 12.7, для объединения файлов XML и XSL потребуется специальный код. предусматривающий возможность использования различных браузеров.
ЛИСТИНГ 12.7. ФУНКЦИЯ LoadXMLXSLTDoc . Я Н Н Н Н Н Ш Н В Н Н
// О Обьявить глобальные переменные
var xmlDoc; var xslDoc; var objOutput;
// © Обнулить переменные
function LoadXMLXSLTDoc(urlXML,urlXSL,elementID){ xmlDoc=null;
xslDoc=null;
//& Определить выходной элемент objOutput = document.getElementById{ elementld);
//О Загрузить файлы XML и XSL
new net.ContentLoader(urlXML,onXMLLoad); new net.ContentLoader(urlXSL,onXSLLoad);
}
// 0 Обработать XML-докуыеит function onXMLLoad(){
xmlDoc=this.req.responseXML;
doXSLT();
}
// 0 Обработать XSL-документ f u n c t i o n onXSLLoad(){
x s l D o c = t h i s . r e q . r e s p o n s e X M L ; doXSLT();
}
// О Проверить, загрузились ли документы

Глава 12 "Живой" поиск с использованием XSLT 495
function doXSLT{){
if (xmlDoc==null || xslDoc==null){ return; }
//© Преобразовать XML-докуыент if (window.ActiveXObject){
objOutput.innerHTML=xmlDoc.transformNode(xslDoc);
}
else{
var xsltProcessor = new XSLTProcessor(); xsltProcessor.importstylesheet(xslDoc);
var fragment =xsltProcessor.transformToFragment( xmlDoc,document);
objOutput.innerHTML = ""; objOutput.appendChild(fragment);
|
} |
_j |
_я |
Чтобы упростить клиентскую часть сценария, необходимо объявить три глобальные переменные О, которые будут вмещать три различных объекта. Первые две переменные, xmlDoc и xslDoc, предназначены для хранения файлов XML и XSL, возвращаемых с сервера. Третья переменная, objOutput, содержит объектную ссылку на элемент DOM, в который будут выводиться результаты. Определив указанные переменные, можно создать функцию LoadXMLXSLTDoc (), вызываемую из функции GrabNumber ().
Поскольку мы загружаем два документа, нужно определить момент, когда оба они будут доступны. Для этого мы проверим, получили ли уже переменные xmlDoc и xslDoc соотнесенные с ними документы. Прежде чем начать, мы должны присвоить обеим этим переменным значение null @. Это гарантирует, что переменные не содержат никаких данных, даже если функция запускается на странице несколько раз. Чтобы задать объект для вывода результата объединения, из вызова функции берется идентификатор переданного элемента ©. Далее мы дважды вызываем функцию ContentLoader — один раз для документа XML и один раз для документа XSL О. При каждом вызове функция ContentLoader получает URL, а затем вызывает другую функцию для загрузки документов. Функция onXMLLoadO © загружает возвращаемый XML-документ в глобальную переменную xmlDoc, а затем вызывает функцию doXSLT () для последующей обработки. Функция onXSLLoadO © загружает XSL-документ в глобальную переменную xslDoc и также вызывает функцию doXSLT ().
Обработка не может продолжаться, пока не будут загружены оба документа, а мы никак не можем определить, который из них будет загружен первым, поэтому функция doXSLT () вначале проверяет, загружены ли оба документа. Она вызывается дважды — после загрузки документа XML и документа XSL. При первом вызове этой функции одна из глобальных переменных все еще имеет значение null, поэтому мы завершаем выполнение функции 0. При следующем вызове выполнение функции не завершается, поскольку ни одна переменная не имеет значения null. Теперь загружены оба документа, и можно выполнять XSLT-преобразование ©.

Глава 12 "Живой" поиск с использованием XSLT 497
рис. 12.4. Отображение результатов "живого" поиска Ajax
Прежде всего необходимо инициализировать объект XSLTProcessor, позволяющий объединять файлы XML и XSL. С помощью метода importStylesheet объекта XSLTProcessor можно импортировать файл XSL, что позволит нам в дальнейшем присоединить его к файлу XML. Загрузив в процессор файл XSL, остается преобразовать XML-документ. Для этого снова используется объект XSLTProcessor, на этот раз с новым методом transformToFragmentO. Метод transformToFragment () принимает файл XML и объединяет его с XSL, возвращая отформатированное конечное дерево.
Для замещения содержимого элемента result свойству innerHTML присваивается значение, равное пустой строке. Таким образом, со страницы удаляется анимация ожидания. Наконец, берется результат, полученный от метода transformToFragment (), и добавляется к элементу result. Теперь пользователь видит отформатированные результаты поиска.
В приведенном коде было введено несколько новых концепций, в том числе объект XSLTProcessor, позволяющий сочетать произвольные файлы XML и XSL. Для объединения подобных документов в браузерах Mozilla и Firefox требуется использовать больше методов DOM. В Internet Explorer для преобразования документа XML нужна единственная строка кода. Окончательные итоги обоих преобразований идентичны: отображаемые результаты поиска отформатированы согласно данным XSL-файла.
Завершив разработку клиентской части сценария, можно сохранить документы и протестировать полученное приложение "живого" поиска. Введите в текстовое окно какой-либо текст и щелкните на кнопке Search ("поиск"). Результаты должны появиться в табличной форме, подобной показанной на рис. 12.4.
Как видите, мы создали XSL-документ и можем успешно производить поиск. Таблица, показанная на рис. 12.4, выглядит довольно скучно, поскольку не содержит форматирования. Это означает, что нам осталось разработать стилевое оформление таблицы результатов, чтобы сделать ее более наглядной. Для этого нам потребуются каскадные таблицы стилей (Cascading Style
Sheets-CSS). |
|
12.5. Последние штрихи |
__ |
Объединив документы XML и XSL для получения результатов, мы должны улучшить стиль отображения результатов поиска, применив к соответствующим элементам правила CSS. Стилевое оформление элементов облегчает восприятие результатов пользователями. Первое, что нужно сделать для улучшения удобства пользователя, — это применить правила CSS к элементам HTML.

498 Часть IV. Ajax в примерах
12.5.1. Применение каскадных таблиц стилей
О каскадных таблицах стилей (Cascading Style Sheets — CSS) речь шла в главе 2. Благодаря им результаты будут выглядеть профессионально при минимальных усилиях с нашей стороны, при этом представление результатов будет отделено от структуры документа и логики преобразования. Если вдруг когда-нибудь менеджера или клиента начнут раздражать выбранные цвета, соответствующие изменения можно будет выполнить легко и быстро. Если мы участвуем в большом проекте с отдельными командами дизайна и кодирования, CSS поможет нам не мешать друг другу. Таблицу стилей можно привязать к поисковой странице как внешний файл, также ее код можно внедрить непосредственно на страницу. Использование внешнего файла CSS предпочтительнее, поскольку он кэшируется браузером и уменьшает время загрузки страницы в будущем. Используемые нами правила таблицы стилей показаны в листинге 12.9.
ШЛистинг 12.9. Каскадная таблица стилей
/ / О Стиль таблицы table{
border: lpx solid black; border-collapse: collapse; width: 50%;
}
// © Стиль ячеек таблицы th, td{
border: lpx solid black; padding: 3px;
width: 25%;
I
// @ Стиль ячеек заголовка th{
_J |
background-color: #A0A0A0; |
|
Первое правило CSS применяется к дескриптору table О. В данном случае мы хотим, чтобы рамка вокруг таблицы формировалась сплошной линией толщиной один пиксель. Свойству border-collapse таблицы присваивается значение collapse. Такая модель CSS, по сути, позволяет унифицировать свойства таблицы. Границы имеют равную толщину — границы соседних ячеек считаются общими, так что между ячейками не возникает границ двойной или тройной толщины. Последним изменением дескриптора table является присвоение значения свойству width. Поскольку столбцов не много, ширина таблицы устанавливается равной 50% ширины содержащего ее элемента div. Все столбцы таблицы будут содержать небольшой объем информации, поэтому большая разрядка в данном случае не нужна.
Определив стиль элемента table, необходимо отформатировать тело и заголовок таблицы ©. Точно так же, как и для всей таблицы, рамку для тела и заголовка мы задаем в виде сплошной линии толщиной один пиксель. Кроме того, добавим небольшое заполнение, чтобы текст не "слипался" с граница-

Глава 12. "Живой" поиск с использованием XSLT 499
Рис. 12.5. Результаты "живого" поиска Ajax с примененными к элементам стилями CSS
ми ячейки. Значение свойства width ячеек мы задаем равным 25% ширины таблицы, чтобы все четыре столбца имели одинаковый размер
Последний этап стилевого оформления таблицы с помощью правил CSS — изменение свойства ячеек заголовка, чтобы визуально отделить их от ячеек тела таблицы. Мы обращаемся к ячейке заголовка © и изменяем цвет ее фона (background-color) на оттенок серого. Здесь же можно изменить и другие свойства — font-weight, color и т.д. Завершив разработку свойств таблицы стилей, мы записываем документ и снова запускаем поиск. Теперь отформатированная таблица выглядит так, как показано на рис. 12.5.
Видно, что таблица имеет структуру, полученную путем применения свойств CSS к элементам таблицы. Если в таблице стилей требуется внедрить больше функциональных возможностей, можно добавить ссылки на файл XSLT и получить еще более гибкую структуру. CSS позволяет настраивать таблицу любым удобным способом, однако поиск можно улучшить и по другим направлениям.
12.5.2. Улучшение поиска
Одним из достоинств Ajax является легкость передачи информации на сервер. Данный проект — это всего лишь упражнение по реализации поиска с использованием Ajax и применением XSLT для отображения таблицы результатов при минимальных усилиях. "Живой" поиск можно совершенствовать до бесконечности. Рассмотрим, например, несколько путей его улучшения.
Включение новых возможностей
В созданной нами форме для выполнения поиска применяется единственное текстовое окно и одна кнопка отправки. Однако мы вполне можем задействовать в поиске множество параметров, например, дополнительно учитывать при поиске имя контактного лица или страны. Все, что для этого требуется, — послать на сервер чуть больше параметров и приказать серверному коду обработать их. Сказанное означает, что пользователи могут искать информацию и с помощью альтернативных вариантов, что сделает форму полезнее.
В данный сценарий можно добавить и другие возможности Ajax, например, реализовав двойные связанные комбинации (см. главу 9), что позволит отсеять результаты, отображаемые для пользователя. Кроме того, можно реализовать технологии, описанные в главе 10, введя в текущий сценарий возможности опережающего ввода.
500 Часть IV Ajax в примерах
Поддержка браузерами Opera и Safari
Напомним, что браузеры Opera и Safari не поддерживают ни метод transformNodeO, ни объект XSLTProcessor. Обеспечить требуемую поддержку можно двумя способами. Первый вариант — использовать Ajax для отправки файлов на сервер, где код серверной части приложения объединит документы XML и XSL. Затем результат преобразования можно извлечь с помощью одного объекта Content Loader, а не двух независимых элементов — данных XML и таблицы стилей XSLT. Такое решение будет не самым лучшим, поскольку для выполнения преобразования клиенту дважды придется обращаться к серверу.
Второй вариант — с помощью Ajax отправить на сервер всю страницу. В таком случае сервер обработает полученный материал и объединит документы XML и XSL. Такой подход лучше, поскольку он позволяет использовать поиск всем пользователям. Если кто-то работает со старой версией браузера, не поддерживающей объект XMLHttpRequest, это не будет препятствовать использованию формы. Если же мы будем полагаться только на технологию Ajax, пользователи, которым она не доступна, не смогут извлечь два требуемых файла для обработки. Поэтому мы примем второй подход, в котором поддержка Ajax не является необходимым условием. Чтобы добавить требуемые функциональные возможности, понадобится в двух местах изменить функцию LoadXMLXSLTDoc {), как показано в листинге 12.10. Вопервых, нужно переписать первый оператор if, добавив проверку наличия процессора XSLT. Во-вторых, следует добавить вариант else, инициирующий отправку формы на сервер.
Листинг 12.10. Функция LoadXMLXSLTDoc G поддержкой Opera и Safari
function LoadXMLXSLTDoc(urlXML,urlXSL,elementID){ var reqXML; var reqXSL;
if {window.XMLHttpRequest && window.XSLTProcessor){
|
//... do Mozilla client XSLT |
|
} else if (window.ActiveXObject){ |
|
//... do Internet Explorer client XSLT |
|
} else{ document.Forml.onsubmit = £unction(){return true;( |
|
document.Forml.submit(); } |
_ > |
Я |
В листинге 12.10 мы переместили обработчик событий onsubmit внутрь ветки else условной проверки, чтобы можно было отправить форму на сервер. Если бы мы не убрали обработчик событий onsubmit, форму невозможно было бы вернуть на сервер.
Далее серверная часть приложения должна выполнить всю необходимую обработку и поместить элемент в форму. В результате мы получим возможность быстрого отклика для тех пользователей, которые могут самостоятельно объединить XSLT с JavaScript, при этом мы не отказываемся от тех пользователей, браузеры которых не поддерживают Ajax или XSLTProcessor. Помните, что Ajax позволяет не визуализировать повторно всю страницу, теряя при этом ее текущее состояние. Благодаря Ajax мы можем не думать