- •Введение
- •Несколько слов о книге
- •Глава 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
- •Формирование объектов
Глава 4. Web-страница в роли приложения 173
Модель на сервере нужна в любом случае, так как именно там находятся важные ресурсы, например, база данных, средства доступа к существующим программам и т.д. Таким образом, модель предметной области на стороне клиента обязана взаимодействовать с моделью на стороне сервера. Что же из этого следует? На этот вопрос мы попытаемся ответить в главе 5, где продоляшм разговор о взаимодействии клиента и сервера. Там же мы затронем вопросы разделения модели предметной области между различными уровнями.
На данный момент мы рассмотрели модель, представление и контроллер без учета их взаимодействия друг с другом. Закончим эту главу обсуждением вопросов совместной работы модели и представления.
4.5. Генерация представления на основе модели
формируя код, выполняемый на стороне клиента, в соответствии с архитектурой MVC, мы получили три отдельные подсистемы. Разделение функций позволяет создавать более понятные программы, но оно же приводит к увеличению объема кода. Противники применения образов разработки аргументируют свою позицию тем, что такой подход способен превратить решение простейшей задачи в сложнейшую процедуру (разработчикам, применяющим Enterprise JavaBeans, такая ситуация хорошо известна).
Создание приложений, насчитывающих несколько уровней, часто приводит к повторению информации на разных уровнях. Мы хорошо понимаем важность принципа DRY и знаем, что наилучший способ избежать дублирования — определять информацию на одном уровне и на ее основании генерировать остальные уровни. В данном разделе мы постараемся применить этот принцип на практике и опишем подход, позволяющий упростить реализацию архитектуры MVC. Обратим внимание на представление.
До сих пор мы рассматривали представление как код, созданный вручную и позволяющий отображать модель. Благодаря такому подходу мы могли достаточно свободно определять, что должен видеть пользователь. Однако в ряде случаев подобная свобода оказывается излишней, а программирование пользовательского интерфейса вручную всегда было скучным и утомительным занятием. Альтернативный подход предполагает автоматическую генерацию пользовательского интерфейса или хотя бы его компонентов на основе модели. Такой принцип уже использовался в среде Smalltalk и в системе Naked Objects Java/.NET. Язык JavaScript также вполне подходит для решения подобных задач. Рассмотрим, что можно сделать средствами отражения JavaScript, и создадим универсальный компонент "Object Browser", который может быть использован в качестве представления для любого объекта JavaScript.
4.5.1. Отражение объектов JavaScript
В большинстве случаев при написании кода для работы с объектом мы имели достаточно полное представление о том, что это за объект и что можно сделать с его помощью. Однако в некоторых случаях приходится создавать
174 Часть II. Основные подходы к разработке приложений
Рис. 4.7. В данном примере ObjectViewer используется для отображения иерархической системы планет, для каждой из которых поддерживается ряд свойств. Кроме того, в массиве хранится дополнительная информация
код "вслепую", не имея предварительных сведений об объекте. Именно так приходится поступать при генерации интерфейса для объектов, составляющих модель. В идеале было бы неплохо реализовать универсальное решение, которое подходило бы для любой предметной области: финансовой деятельности, электронной коммерции, визуализации результатов научных исследований и т.д. В данном разделе вы ознакомитесь с JavaScript-библиотекой ObjectViewer, которую вы, может быть, захотите использовать при разработке своих приложений. Для того чтобы помочь вам составить представление об этой библиотеке, на рис. 4.7 показано несколько уровней сложного графа, отображаемых с помощью ObjectViewer.
Объект, выбранный для просмотра, представляет планету Меркурий. Этот объект достаточно сложен. Помимо обычных свойств, значениями которых являются числа или строки, он содержит URL изображения и массив с дополнительными данными. ObjectViewer обрабатывает всю информацию, не имея предварительных сведений о типе объекта.
Процесс исследования объекта, выяснения его свойств и возможностей называется отражением (reflection). Читатели, имеющие опыт работы с Java или .NET, уже знакомы с этим термином. Возможности отражения, реализованные в языке JavaScript, мы рассмотрим подробно в приложении Б. Здесь же только скажем, что объект JavaScript можно исследовать так, как будто он представляет собой ассоциативный массив. Приведенный ниже фрагмент кода выводит информацию о свойствах объекта.
var description»""; for (var i in MyObj){
var property=MyObj[i];
description+=i+" = "+property+"(BBSS)n"; } alert(description);
Представление данных посредством окна, предназначенного для вывода сообщений, — не самое удачное решение, так как это окно может не сочетаться с остальными компонентами интерфейса. Мы выбрали такой подход лишь для того, чтобы упростить пример. В листинге 4.11 представлен код ObjectViewer.
Глава 4. Web-страница в роли приложения 175
Листинг4.11.БиблиотекаObjectViewer
objviewer.ObjectViewer=function(obj,div,isInline,addNew){
styling.removeAllChildren(div); this.obj ect=obj; this.mainDiv=div; this.mainDiv.viewer=this ; this.islnline=islnline; this.addNew=addNew;
var table=document.createElement("table"); this.tbod=document.createElement("tbody"); table.appendChild(this.tbod); this.fields=new ArrayO;
this.children=new ArrayO; for (var i in this.object){
this.fields[i]=new objviewer.PropertyViewer( this, i
);
}
objviewer.PropertyViewer=function(obj ectViewer,name){ this.objectViewer=objectViewer;
this.name=name; this.value=objectViewer.object[this.name] ; this.rowTr=document.createElement("tr") ;
this.rowTr.className='objViewRow' ; this.valTd=document.createElement("td");
this.valTd.className='objViewValue' ; this.valTd.viewer=this; this.rowTr.appendChild(this.valTd) ; var valDiv=this.renderSimple(); this.valTd.appendChild(valDiv); viewer.tbod.appendChild(this.rowTr);
}
objviewer.PropertyViewer.prototype.renderSimple=function() { var valDiv=document.createElement("div");
var valTxt=document.createTextNode(this.value); valDiv.appendChild(valTxt);
if (this.spec.editable){ valDiv.className+=" editable"; valDiv.viewer=this;
valDiv.onclick=objviewer.PropertyViewer.editSimpleProperty;
}
return valDiv;
}
Всоставбиблиотекивходятдваобъекта:ObjectViewer,предназначенный Для просмотра объекта и формирования HTML-таблицы, и PropertyViewer, который отображает имя и значение отдельного свойства и представляет их
ввиде строки таблицы.
Вцелом задача решена, но существует ряд проблем. Во-первых, ОЬ- j ectViewer исследует каждое свойство. Если вы добавите к прототипу вспомогательные функции, Obj ectViewer обнаружит их. Если вы обработаете таким способом узел D O M , то увидите все встроенные свойства и сможете оценить, насколько сложен элемент D O M . В большинстве случаев нет необхо-
176 Часть II. Основные подходы к разработке приложений
димости представлять пользователю все свойства объекта. Выбрать свойства для отображения можно, присваивая объекту перед воспроизведением специальное свойство, представляющее собой массив. Этот подход иллюстрируется кодом, приведенным в листинге 4.12.
Листинг 4.12. Применение свойства objviewspec
objviewer.ObjectViewer=function(obj,div,isInline,addNew){ styling.removeAllChildren(div) ;
this.object=obj;
this.spec=objviewer.getSpec(obj);
this.mainDiv=div;
this.mainDiv.viewer=this;
this.islnline=islnline;
this.addNew=addNew;
var table=document.createElement("table"); this.tbod=document.createElement("tbody") ; table.appendChiId(this.tbod);
this. fields=new Array (); this.children=new Array();
for (var i=0,-i<this.spec.length;i++){ this.fields[i]=new objviewer.PropertyViewer(
this,this.specfi]
);
}
objviewer.getSpec=function (obj){ return (obj.objViewSpec) ?
obj.objViewSpec : objviewer.autoSpec(obj) ;
} objviewer.autoSpec=function(obj){ var members=new Array();
for (var propName in obj){ var spec={name:propName}; members.append(spec);
}
return members;
}
obj viewer.PropertyViewer=function(obj ectViewer,memberSpec) { this.objectViewer=objectViewer;
this.spec=memberSpec;
this.name=this.spec.name;
}
Мы определили свойство objViewSpec, которое используется конструктором Objectviewer. Если данное свойство отсутствует, оно формируется в функции autoSpecO путем анализа объекта. Свойство objViewSpec представляет собой массив, каждый элемент которого является таблицей свойств. В данном случае мы генерируем только свойство name. Конструктору PropertyViewer передается описание свойства, из которого можно извлечь инструкции о порядке воспроизведения.
Предоставляя спецификацию для объекта, обрабатываемого Objectviewer, мы можем ограничить набор отображаемых свойств.