Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ajax_v_deystvii.pdf
Скачиваний:
34
Добавлен:
05.03.2016
Размер:
5.83 Mб
Скачать

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]