- •Введение
- •Несколько слов о книге
- •Глава 1. Каким должен бытъ Web-интерфейс
- •Действия пользователя при работе с приложением
- •Накладные расходы при работе в сети
- •Асинхронное взаимодействие
- •Независимый и переходный образы использования
- •Четыре основных принципа Ajax
- •Браузер имеет дело с приложением, а не с содержимым
- •Сервер доставляет данные, а не содержимое
- •Реальное кодирование требует порядка
- •Применение богатых клиентов Ajax
- •Системы, созданные с использованием Ajax
- •Google Maps
- •Альтернативные технологии
- •Macromedia Flash
- •Java Web Start
- •Резюме
- •Ресурсы
- •Основные элементы Ajax
- •JavaScript изучался не зря
- •Определение внешнего вида с помощью CSS
- •Селекторы CSS
- •Свойства стилей
- •Простой пример использования CSS
- •Обработка DOM с помощью JavaScript
- •Поиск узла DOM
- •Создание узла DOM
- •Добавление стилей к документу
- •Свойство innerHTML
- •Асинхронная загрузка с использованием XML
- •Элементы IFrame
- •Объекты XmlDocument и XMLHttpRequest
- •Использование фуниции обратного вызова для контроля запроса
- •Жизненный цикл процедуры поддержки запроса
- •Отличия Ajax от классических технологий
- •Резюме
- •Ресурсы
- •Порядок из хаоса
- •Образы разработки
- •Реструктуризация и Ajax
- •Во всем надо знать меру
- •Реструктуризация в действии
- •Варианты применения реструктуризации
- •Несоответствие браузеров: образы разработки Fagade и Adapter
- •Управление обработчиками событий: образ разработки Observer
- •Повторное использование обработчиков событий: образ разработки Command
- •Обеспечение единственной ссылки на ресурс: образ разработки Singleton
- •"Модель-представление-контроллер "
- •Серверная программа Ajax, созданная без применения образов разработки
- •Реструктуризация модели
- •Разделение содержимого и представления
- •Библиотеки независимых производителей
- •Библиотеки, обеспечивающие работу с различными браузерами
- •Компоненты и наборы компонентов
- •Элементы, располагаемые на стороне сервера
- •Резюме
- •Ресурсы
- •Применение архитектуры MVC к программам различных уровней
- •Применение архитектуры MVC к объектам, присутствующим в среде браузера
- •Представление в составе Ajax-приложения
- •Отделение логики от представления
- •Отделение представления от логики
- •Контроллер в составе Ajax-приложения
- •Классические JavaScript-обработчики
- •Модель обработки событий W3C
- •Реализация гибкой модели событий в JavaScript
- •Модель в составе Ajax-приложения
- •Использование JavaScript для моделирования предметной области
- •Взаимодействие с сервером
- •Генерация представления на основе модели
- •Отражение объектов JavaScript
- •Обработка массивов и объектов
- •Включение контроллера
- •Резюме
- •Ресурсы
- •Программы, выполняемые на сервере
- •Создание программ на стороне сервера
- •N-уровневые архитектуры
- •Управление моделью предметной области на стороне клиента и на стороне сервера
- •Принципы создания программ на сервере
- •Серверные программы, не соответствующие основным принципам разработки
- •Использование архитектуры Model!
- •Использование архитектуры на базе компонентов
- •Архитектуры, ориентированные на использование Web-служб
- •Частные решения: обмен данными
- •Взаимодействие, затрагивающее только клиентскую программу
- •Пример отображения информации о планетах
- •Взаимодействие, ориентированное на содержимое
- •Взаимодействие, ориентированное на сценарий
- •Передача данных серверу
- •Использование HTML-форм
- •Использование объекта XMLHttpRequest
- •Управление обновлением модели
- •Резюме
- •Ресурсы
- •Создание качественного приложения
- •Отклик программы
- •Надежность
- •Согласованность
- •Простота
- •Как получить результат
- •Предоставление сведений пользователю
- •Поддержка ответов на собственные запросы
- •Обработка обновлений, выполненных другими пользователями
- •Создание системы оповещения
- •Основные принципы оповещения
- •Реализация базовых средств оповещения
- •Отображение пиктограмм в строке состояния
- •Отображение подробных сообщений
- •Формирование готовой системы
- •Предоставление информации в запросах
- •Информация о новизне данных
- •Простой способ выделения данных
- •Выделение данных с использованием библиотеки Scriptaculous
- •Резюме
- •Ресурсы
- •JavaScript и защита браузера
- •Политика "сервера-источника"
- •Особенности выполнения сценариев в Ajax-приложении
- •Проблемы с поддоменами
- •Взаимодействие с удаленным сервером
- •Взаимодействие с Web-службами
- •Защита конфиденциальной информации
- •Вмешательство в процесс передачи данных
- •Организация защищенного НТТР-взаимодействия
- •Передача шифрованных данных в ходе обычного HTTP-взаимодействия
- •Управление доступом к потокам данных Ajax
- •Создание защищенных программ на уровне сервера
- •Ограничение доступа к данным из Web
- •Резюме
- •Ресурсы
- •Что такое производительность
- •Скорость выполнения JavaScript-программ
- •Определение времени выполнения приложения
- •Использование профилировщика Venkman
- •Оптимизация скорости выполнения Ajax-приложения
- •Использование памяти JavaScript-кодом
- •Борьба с утечкой памяти
- •Особенности управления памятью в приложениях Ajax
- •Разработка с учетом производительности
- •Простой пример управления памятью
- •Как уменьшить объем используемой памяти в 150 раз
- •Резюме
- •Ресурсы
- •Сценарий двойной комбинации
- •Недостатки клиентского решения
- •Недостатки клиентского решения
- •Архитектура клиента
- •Разработка взаимодействия клиент/сервер
- •Реализация сервера: VB.NET
- •Написание кода сервера
- •Представление результатов
- •Применение каскадных таблиц стилей
- •Дополнительные вопросы
- •Запросы при выборе нескольких элементов
- •Переход от двойного связного выбора к тройному
- •Реструктуризация
- •Новый и улучшенный объект netContentLoader
- •Создание компонента двойного списка
- •Резюме
- •Глава 10. Опережающий ввод
- •Изучаем опережающий ввод
- •Типичные элементы приложений опережающего ввода
- •Google Suggest
- •Ajax как средство опережающего ввода
- •Структура серверной части сценария: С#
- •Сервер и база данных
- •Тестирование серверного кода
- •Структура клиентской части сценария
- •HTML
- •JavaScript
- •Обращение к серверу
- •Дополнительные возможности
- •Реструктуризация
- •День 1: план разработки компонента TextSuggest
- •День 3: включаем Ajax
- •День 4: обработка событий
- •День 5: пользовательский интерфейс всплывающего окна с предлагаемыми вариантами
- •Итоги
- •Резюме
- •Эволюционирующий портал
- •Классический портал
- •Портал с богатым пользовательским интерфейсом
- •Создание портала с использованием Java
- •Таблица пользователя
- •Серверная часть кода регистрации: Java
- •Структура регистрации (клиентская часть)
- •Реализация окон DHTML
- •База данных окон портала
- •Серверный код окна портала
- •Добавление внешней библиотеки JavaScript
- •Возможность автоматического сохранения
- •Адаптация библиотеки
- •Автоматическая запись информации в базе данных
- •Реструктуризация
- •Определение конструктора
- •Адаптация библиотеки AjaxWindows.js
- •Задание команд портала
- •Выводы
- •Резюме
- •Понимание технологий поиска
- •Классический поиск
- •"Живой" поиск с использованием Ajax и XSLT
- •Возврат результатов клиенту
- •Код клиентской части сценария
- •Настройка клиента
- •Инициализация процесса
- •Код серверной части приложения: РНР
- •Создание XML-документа
- •Создание документа XSLT
- •Объединение документов XSL и XML
- •Совместимость с браузером Microsoft Internet Explorer
- •Совместимость с браузерами Mozilla
- •Последние штрихи
- •Применение каскадных таблиц стилей
- •Улучшение поиска
- •Поддержка браузерами Opera и Safari
- •Использовать ли XSLT
- •Решение проблемы закладок
- •Реструктуризация
- •Объект XSLTHelper
- •Компонент "живого" поиска
- •Выводы
- •Резюме
- •Считывание информации из внешнего мира
- •Поиск XML-лент
- •Изучение структуры RSS
- •Богатый пользовательский интерфейс
- •Чтение лент
- •HTML-структура без таблиц
- •Гибкое CSS-форматироеание
- •Глобальный уровень
- •Предварительная загрузка средствами Ajax
- •Богатый эффект перехода
- •Правила прозрачности, учитывающие индивидуальность браузеров
- •Реализация затухающего перехода
- •Интеграция таймеров JavaScript
- •Дополнительные возможности
- •Введение дополнительных лент
- •Интеграция функций пропуска и паузы
- •Как избежать ограничений проекта
- •Обход системы безопасности браузеров Mozilla
- •Изменение масштаба приложения
- •Реструктуризация
- •Модель приложения
- •Представление приложения
- •Контроллер приложения
- •Выводы
- •Резюме
- •Отладчики
- •Для чего нужен отладчик
- •Средство Safari DOM Inspector для Mac OS X
- •Ресурсы
- •JavaScript — это не Java
- •Формирование объектов
548 Часть IV. А/'ах в примерах
установить флажок Remember this decision ("Запомнить мой выбор"). Таким образом, браузер запоминает ваш выбор и позволяет выполнять объект XMLHttpRequest без запроса подтверждения.
Изменив настройки безопасности браузера, мы можем запустить приложение на рабочем столе, используя браузеры Mozilla, Firefox и Netscape. Используя данное приложение, мы можем обращаться к ХМ1^лентам с любого сайта, не открывая несколько закладок или окон. Кроме того, приложение можно изменить, чтобы оно получало из Web другую информацию, например, прогноз погоды и комиксы.
13.6.2. Изменение масштаба приложения
Наше приложение может не только читать XML-ленты, извлекаемые с различных сайтов. Мы легко можем использовать его как переключатель рекламных объявлений в баннере, сценарий обновления новостей компании, календарь событий и многое другое.
Например, мы можем хранить баннерную рекламу в документе XML. Таким образом в XML-файл легко ввести новую рекламу, не затрагивая HTMLфайлы или серверный код. Мы можем предварительно загрузить баннерную рекламу, а затем отобразить ее в нашем приложении. Вместо того чтобы на экране находился один рекламный ролик, мы можем циклически менять их, пока пользователь изучает сайт.
Мы можем поместить в XML-документ новости компании, чтобы отображать текущие статьи сотрудникам или клиентам. Все, что от нас требуется, — это заполнить стандартные элементы XML-ленты. Кроме того, можно отображать информацию об обновлении сайта или любые другие данные. Как видите, возможности приложения не ограничиваются простым чтением XML-лент.
13.7. Реструктуризация
Итак, мы получили готовый сценарий для чтения RSS-лент и можем, как обычно, заняться его улучшением. Как отмечалось ранее, существуют богатые возможности расширения сценария с точки зрения представления различных типов содержимого. В данном разделе мы сфокусируем внимание на реорганизации сценария согласно архитектуре "модель-представление- контроллер" (Model-View-Controller — MVC). Как объяснялось в главах 3 и 4, MVC широко распространена и позволяет разделять обязанности между различными фрагментами кода. Свое обсуждение мы начнем с определения типов моделей, затем создадим представление модели и разработаем контроллер, связывающий в единое целое все компоненты.
Глава 13 Создание приложений Ajax, не использующих сервер 549
13.7.1. Модель приложения
Разработанное выше приложение чтения RSS-лент очевидно только выиграет от наличия нескольких типов модели. Благодаря этому программное обеспечение будет концептуально понятнее, его станет легче использовать и модифицировать. Поскольку приложения Ajax возлагают более интенсивную нагрузку на DHTML-код клиента, чем традиционные Web-приложения, важность понятного управляемого кода становится первостепенной. Классы модели, которые мы собираемся разработать, должны быть в общем случае применимы и к другим приложениями, работающим с RSS-лентами. Чтобы немного упростить синтаксис кода, для определения типов мы, как и в главе 10, будем использовать библиотеку Prototype.
Начнем с определения класса модели для RSS-ленты. В данном случае RSS-лента представляет собой XML-документ, обладающий предопределенной структурой и имеющий URL, задающий путь доступа к этому документу. Основными атрибутами структуры являются название, ссылка и описание, кроме того, как обсуждалось выше, имеется множество дополнительных атрибутов. Кроме того, лента содержит несколько элементов item, которые можно рассматривать как набор статей. Для начала давайте соберем все, что мы знаем о ленте, в такой форме, как показано в листинге 13.21.
Листинг 13.21. Класс модели RSSFeed • . ^ДВИЯДЦ^И^^ИЭД^^^^Р RSSFeed = Class.create();
RSSFeed.prototype = {
initialize: function( title, link, description ) { this.title - title;
this.link = link; this.description = description; this.items = [];
Ь
addltem: function(anltem) { this.iterns.push(anltem); }
В приведенном коде тип RSSFeed определяется посредством функции Class.create() библиотеки Prototype. Напомним, что, используя эту идиому, мы с помощью сгенерированного конструктора вызываем метод i t n i - tialize . Таким образом, при данном определении класса модели RSS-ленты ленту можно создать с помощью следующей строки кода:
var rssFeed = new RSSFeed{ 'JavaRanch News', 'http://radio.javaranch.com/news/', 'Stories from around the ranch' );
По сути, это все, что требуется для определения объекта модели RSSFeed. Обратите внимание на то, что RSSFeed имеет API addltem, разрешающий добавление статей к исходному массиву элементов. Каждая статья должна представлять собой объект модели, который инкапсулирует атрибуты каждого элемента ленты. Вспомнив все, что мы знаем о статьях RSS, можно определить класс модели статьи, как показано в листинге 13.22.
550 Часть IV Ajax в примерах
Листинг 13.22. Класс модели RSSltem
RSSItem = Class.create(); RSSltem.prototype = {
initialize: function( rssFeed, title, link, description ) {
|
this.rssFeed = rssFeed,- |
||
|
this.title = |
title; |
|
|
this.link = |
link; |
|
|
this.description = description; |
||
|
J |
|
|
}; |
|
_ |
я |
Ничего особенного. В элемент статьи инкапсулированы атрибуты t i t l e , link и description, кроме того, имеется ссылка на объект RSSFeed, к которому относится статья. Изучая два описанных класса, видим, что статью и одну из лент можно сформировать следующим образом:
var rssFeed = new RSSFeed( 'JavaRanch News', 'http://radio.javaranch.com/news/', 'Stories from around the ranch' );
var feedl = new RSSItem( rssFeed, 'Win a copy of JBoss',
' h t t p : / / r a d i o . j a v a r a n c h . e o m / n e w s / 0 5 / 0 7 / 2 0 / 9 . h t m l ' , 'Text o f A r t i c l e ' ) ;
r s s F e e d . a d d l t e r n ( f e e d l ) ;
Пока что все хорошо. Модель является довольно прямолинейной инкапсуляцией атрибутов RSS-ленты и ее статей. Данные концепции инкапсулируются в двух классах модели —- RSSFeed и RSSltem. Далее мы рассмотрим собственно построение модели. Мы знаем, что эти объекты будут обработаны в результате загрузки XML-данных клиенту в ответ на запрос Ajax. Поэтому мы определим программный интерфейс, вызывая который наш обработчик Ajax сможет преобразовать XML-отклик в экземпляр класса модели RSSFeed. Для этого мы определим вначале контракт процесса, формирующего модель.
var rssFeed = RSSFeed.parseXML{ rssXML );
Данный контракт подразумевает, что мы передаем XML-отклик, возвращаемый обработчиком Ajax, методу синтаксического разбора нашего приложения RSSFeed; в результате мы должны получить экземпляр RSSFeed. Будем придерживаться сказанного и реализуем метод parseXML(), как показано в листинге 13.23.
RSSFeed.parseXML = function(xmlDoc) { var rssFeed = new RSSFeed(
RSSFeed.getFirstValue(xmlDoc, 'title'),
RSSFeed.getFirstValue(xmlDoc, 'link' ), RSSFeed.getFirstValue(xmlDoc, 'description'));
var feedltems = xmlDoc.getElementsByTagName('item'); for ( var i = 0 ; i < feedltems.length ; i++ ) {
rssFeed.addltern(new RSSltem(rssFeed, RSSFeed.getFirstValue(feedltems[i], 'title'), RSSFeed.getFirstValue(feedltems[i], 'link' ), RSSFeed.getFirstValue(feedltems[i], 'description'))
|
Глава 13 Создание приложений Ajax, не использующих сервер |
551 |
|
} |
|
|
|
|
r e t u rn rssFeed; |
|
|
} |
|
m |
|
Приведенный метод выполняет классический разбор XML-отклика, с которым вы уже неоднократно сталкивались. Метод принимает значения элементов t i t l e , link и description, используя их для создания RSSFeed. Затем ои последовательно проходит по всем элементам item, выполняя эти же действия и создавая для каждого элемента экземпляр RSSltem. На каждой итерации используется метод addltemO, добавляющий статью к родительской RSS-ленте. Обратите внимание на использование вспомогательного метода для получения значения узла первого дочернего элемента с данным именем дескриптора. Данный вспомогательный метод getFirstValue показан в листинге 13.24.
Листинг 13.24. Вспомогательный метод синтаксического разбора
RSSFeed.getFirstValue = |
function(element, |
tagName) { |
||
var |
children |
= element.getElementsByTagName(tagName); |
||
if { |
children |
== null |
I | children, length |
*== 0 ) |
return "";
if ( children[O] - firstChild && children[O].firstChild.nodeValue )
return children[O].firstChild.nodeValue;
} |
return |
""; |
|
я |
Это все, что нам требуется с точки зрения модели. Очевидно, мы можем добавить атрибуты, представляющие все необязательные элементы RSSленты, и присвоить им значения, если эти элементы присутствуют в ленте. В данном случае нам это делать не нужно, поскольку приложение чтения RSS-лент не использует дополнительные атрибуты (и не требует их предоставления). Тем не менее возможность предоставления дополнительных метаданных существует. Кроме того, мы можем определить методы, предлагающие более формальный контракт доступа к атрибутам. Например, мы можем написать пару getTitle( )/setTitle(), предназначенную для доступа к атрибуту заголовка. Поскольку JavaScript, в отличие от других объектноориентированных языков, не поддерживает визуальную семантику (например, ключевые слова private/protected в Java), мы этим вопросом заниматься не будем. Итак, с моделью мы закончили, переходим к представлению.
13.7.2. Представление приложения
Разобравшись с классами модели, мы можем рассматривать класс представления. Мы можем разработать один класс представления для RSSFeed, а другой — для RSSltem, но поскольку приложение RSSReader в действительности не представляет ленту независимо, мы определим один класс представления RSSltemView, который инкапсулирует представление RSSltem в контексте родительского элемента RSSFeed Поскольку в данном случае представ-
552 Часть IV. Ajaxв примерах
ление — это HTML-код, наш класс представления отвечает за генерацию HTML-страницы. Итак, рассмотрим для начала конструктор, представленный в листинге 13.25-
Листинг 13.25. Класс представления RSSItemView
RSSItemView = C l a s s . c r e a t e ( ) ; |
|
|
RSSItemView.prototype = { |
|
|
i n i t i a l i z e : function(rssltem, |
feedlndex, itemlndex, numFeeds) { |
|
t h i s . r s s l t e m = |
rssltem; |
|
t h i s . f e e d l n d e x |
• feedlndex |
+ 1; |
t h i s . i t e m l n d e x = i t e m l n d e x + 1; |
||
t h i s . n u m F e e d s |
= numFeeds; |
|
Ь
}
Давайте внимательно рассмотрим параметры. Первый параметр — экземпляр RSSltem. С его помощью мы сообщаем представлению, для какого экземпляра модели обеспечивается это представление. Обратите внимание на то, что в общем случае классам модели не разумно знать что-либо о представлении, однако представление по необходимости имеет детальные знания о модели. Другие параметры обеспечивают некоторый дополнительный контекст для представления. Параметр feedlndex сообщает представлению номер ленты. Параметр itemlndex указывает представлению, где размещается данная статья в родительском массиве статей RSSFeed. Параметр numFeeds сообщает представлению, сколько всего лент. Все указанные параметры необходимы для получения информации о месте данного представления в мире. Представление может пожелать отобразить область содержимого, указывающую, например, "это лента 1 из 7 и статья 3 из 5". Данные атрибуты можно внедрить в модель, однако в действительности они не являются атрибутами, за которые в общем случае должна отвечать модель, поэтому этот контекст (требуемые представлению) передается клиентом в конструктор представления.
Как отмечалось выше, представление отвечает за генерацию HTML-кода. Поэтому нашему классу представления понадобится еще один метод. Посмотрим, как это он выглядеть (листинг 13.26).
Листинг 13.26. Метод генерации HTML-кода
toHTML: function () { var out = ""
out += '<span class="rssFeedTitlePrompt">RSS Feed '
out += '(' + this.feedlndex + • of ' + this.numFeeds + ') : '; out += '</span>';
out += '<span class="rssFeedTitle">';
out += '<a href="' + this.rssltem.rssFeed.link + '">' + this.rssltem.rssFeed.title + '</a>';
out += '</span>'; out += '<br/>';
out += '<span class="rssFeedItemTitlePrompt">Article '; out += '(' + this.itemlndex + ' of * + this.rssltem.rssFeed.items.length + ') : ' ;
out += '</span>';
Глава 13. Создание приложений Ajax, не использующих сервер 553
Рис. 13.15. RSS-лента (х из у): название RSS-ленты
o u t + = ' < s p a n c l a s s = " r s s F e e d I t e m T i t l e " > ' ;
o u t += ' < a h r e f - " 1 + t h i s . r s s l t e m . l i n k + ' " > ' + t h i s . r s s l t e m . t i t l e + ' < / a > ' ;
o u t + = ' < / s p a n > ' ;
o u t + = ' < d i v c l a s s = " r s s I t e m C o n t e n t " > ' ; o u t + - t h i s . r s s l t e m . d e s c r i p t i o n ;
o u t + = ' < / d i v > ' ;
r e t u r n o u t ;
Ь
Метод toHTML создает на экране контекстные элементы, за которыми сле-
дует текст статьи. Первый фрагмент кода отображает текст RSS-лента (х |
из |
у) : Заголовок RSS-ленты. Атрибут link родительского элемента rssFeed |
ис- |
пользуется для генерации атрибута HREF анкера, а атрибут t i t l e — для генерации текста анкера. Для каждого элемента span генерируется имя класса CSS (одно для запроса, второе для анкера), что позволяет независимо определять стили этих элементов. Сказанное иллюстрируется на рис. 13.15.
Следующий фрагмент кода генерирует текст Статья (х из у) : Заголовок RSS-статьи. Для генерации атрибута HREF анкера используется атрибут link RSS-статьи, а с помощью атрибута t i t l e статьи генерируется текст анкера. Кроме того, как показано на рис. 13.16, код предоставляет имена классов CSS для запроса и заголовка. Несколько последних строк метода toHTML генерируют элемент div, который будет вмещать содержимое статьи RSSltem (атрибут description). Соответствующий код выглядит следующим образом:
out += '<div class="rssItemContent">'; out += this.rssltem.description;
out += '</div>';
Для текста статьи генерируется имя класса CSS rssltemContent. Чтобы текст не подходил вплотную к границе блока, в соответствующем классе необходимо задать небольшие поля и заполнение. Кроме того, данный блок должен иметь фиксированную высоту и значение auto параметра overflow, чтобы его содержимое можно было при необходимости прокручивать независимо от показанной ранее контекстной информации. Типичное определение класса CSS для блока текста статьи приводится ниже.
554 Часть IV. Ajax в примерах
Рис. 13.16. Статья (х из у) заголовок RSS-статьи
Рис. 13.17. Статья (х из у) заголовок RSS-статьи
.rssItemContent {
border-top : lpx solid black; width : 590px;
height : 350px; overflow : auto; padding : 5px; margin-top : 2px;
}
При таком стилевом оформлении область содержимого должна выглядеть примерно так, как показано на рис. 13.17. Собирая вместе все составляющие, получаем показанное на рис. 13.18 представление, генерируемое классом RSSitemView. Прежде чем завершить рассмотрение представления, добавим в него еще один маленький метод, который немного облегчит его использование.
toString: function() { return this.toHTMLO;
J