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

Все, что делает метод, — проверяет, равно ли значение состояния readys- ' t a t e четырем (указывает на завершение), и уведомляет t h i s , component, что отклик доступен. Но мы еще не закончили. Существовало еще требование соответствующей обработки ошибок. Но что такое "соответствующая"? Этого мы сказать не можем. Обработка ошибок — это решение, которое нужно отложить для другого объекта. Следовательно, мы предполагаем, что наш клиент this, component, имеет метод handleError, который и выполняет необходимые действия, когда отклик Ajax поступает как-то не так. Этот компонент может в свою очередь делегировать решение другому объекту, но это уже не проблемы нашего вспомогательного объекта. Мы обеспечили механизм и позволим другому объекту обеспечить семантику. Как говорилось ранее мы предполагаем, что this . component имеет методы aj axupdate и handleError. Это неявный созданный нами контракт, поскольку JavaScript не относится к языкам со строгим контролем типов, которые могут принудительно вводить подобные ограничения.

Поздравляем! Вы трансформировали net .ContentLoader Б гибкий вспомогательный объект, который выполняет всю грязную работу Ajax для ваших DHTML-компонентов, лоддерживающих Ajax. А если у вас есть DHTMLкомпонент, который еще не поддерживает Ajax, теперь он станет проще! Кстати, нам еще нужно написать компонент двойных списков.

9.6.2. Создание компонента двойного списка

Мы немного поработали с объектом net.ContentLoader, чтобы существенно упростить нашу задачу, к которой теперь пора вернуться. Предположим, что нам, первоклассным разработчикам, поручили создать сценарий Связанного выбора, который можно использовать повторно во многнх^онтекстах в приложении или нескольких приложениях. В связи с этим для удовлетворения поставленным требованиям необходимо рассмотреть несколько вопросов.

• Предположим, мы не можем (или не хотим) вручную менять HTML-

. разметку окон выбора. Так может быть, например, если мы не отвечаем за производство разметки. Возможно, элемент select генерируется JSP или другим дескриптором на языке сервера. Возможно, какой-то дизайнер написал HTML-код, а мы желаем по возможности сохранить его без изменений, чтобы не допустить дополнительного этапа переверстки страницы.

Нам требуется сценарий связывания, который может использовать различные указатели URL и параметры запроса для возврата данных option. Кроме того, требуется, чтобы структура допускала будущую настройку.

Необходимо, чтобы поведение, реализованное в сценарии зависимого выбора, можно было потенциально использовать в нескольких наборах дескрипторов select на одной странице, а также, чтобы существовала возможность реализации тройных или четверных комбинаций, как обсуждалось ранее.

Начав с первой задачи (максимально сохранить HTML-разметку), предположим, что характерным представителем HTML-кода, с которым нам придется работать, является разметка, приведенная в листинге 9.10.

<html>

<body>

<form name="Forml">

<select id="region" name="region" > <options... >

</select>

<select i d = " t e r r i t o r y " name="territory" /> </form>

</body>

</html>

Нам нужен компонент DoubleCombo, который можно присоединить к документу, возложив на него всю магию двойной комбинации. Поэтому пойдем с конца и рассмотрим, как бы нам хотелось, чтобы выглядела разметка, а затем сообразим, как ее реализовать. Изменим разметку так, чтобы она выглядела подобно коду, приведенному в листинге 9.11.

Листинг 9.11. Модифицированная HTML-разметка . '5 !а ТЯИЯНИИТТ

<html>

<head>

<script>

function inj ectComponentBehaviors() { var doubleComboOptions = {};

< ! — Компонент DoubleCombo —>

new DoubleCombo('region', 'territory',

1DoubleComboXML.aspx', doubleComboOptions );

}

 

</script>

 

</head>

 

<body onload=

 

•injectComponentBehaviors()"

 

>

 

<form name="Forml">

 

<select id="region" narae="region" >

 

<option value="-l">Pic]c A Region</option>

 

<option value="1">Eastern</option>

 

<option value="2">Western</option>

 

<option value="3">Northern</option>

 

<option value="4">Southern</option>

 

</select>

 

<select id="territory" name="territory" />

 

</form>

 

</body>

 

</html>

д

Листйнг-9,12. Компонент DoubieComboDoubleCombo function DoubleCombo( masterld, slaveld, url, options ) {
// Инициализация состояния
t h i s . m a s t e r - d o c u m e n t . g e t E l e m e n t B y l d ( m a s t e r l d ) ; t h i s . s l a v e = d o c u m e n t . g e t E l e m e n t B y I d ( s l a v e l d ) ;
t h i s . o p t i o n s = o p t i o n s ;
t h i s . a j a x H e l p e r " new n e t . C o n t e n t L o a d e r ( t h i s , u r l , "POST", o p t i o n s . r e q u e s t P a r a m e t e r s I I, И
// Инициализация поведения - — t h i s . i n i t i a l i z e B e h a v i o r ( ) ;
}

Разметка изменилась следующим образом

Создана функция, инжектирующая в документ требуемое нам поведение

К элементу тела, вызывающему данную функцию, добавлен обработчик onload.

Обратите внимание на то, что в элементе <body> страницы никаких модификаций нет. Как отмечалось ранее — это хорошо. Таким образом, мы уже удовлетворили первому требованию. Но давайте посмотрим на функцию injectComponentBehaviors() — похоже, что есть еще одна работа. Действительно, нам нужно создать объект JavaScript, именуемый DoubleCombo в котором было бы реализовано все поведение, необходимое для поддержки функциональных возможностей двойной комбинации.

Логика компонента DoubleCombo

Для начала рассмотрим внимательнее семантику создания нашего компонента. Функция injectComponentBehaviors() создает объект DoubleCombo, вызывая его конструктор. Этот конструктор определен в листинге 9.12.

. .

);

m

Данная конструкция должна быть вам уже знакомой; наша функция конструктора инициализирует состояние объекта DoubleCombo. Описания аргументов, которые необходимо передать конструктору, приведены в табл. 9.2.

Рассмотрим природу состояния, поддерживаемого объектом DoubleCombo, особенно URL и опции. Две данные составляющие состояния удовлетворяют второму указанному ранее функциональному требованию, т.е. наш компонент может задействовать для извлечения данных любой URL, кроме того, этот компонент можно настраивать с помощью параметра options. Пока что мы предполагаем, что в объекте опций найдем только одно — свойство requestParameters. Однако, поскольку параметр options — это просто общий объект, мы можем приписать ему любое свойство, необходимое для облегчения последующей настройки. Наиболее очевидными "довесками" объекта опций являются стилевое оформление классов CSS и прочее из этой же серии. Однако стиль и функции связных списков очевидно являются независимыми концепциями, поэтому решение вопросов стилевого оформления мы оставляем дизайнеру страницы.

Таблица 9.2. Описание аргументов дргумент Описание

m a s t e r l d Идентификатор элемента разметки, соответствующего основному элементу s e l e c t . Выполнение операции выбора на данном элементе определяет значения, отображаемые вторым элементом s e l e c t

s l a v e l d Идентификатор элемента разметки, соответствующею зависимому элементу s e l e c t . Значения этого элемента будут меняться в зависимости от выбора пользователем значения основного элемента s e l e c t

o p t i o n s Общий объект, предоставляющий другие данные, требуемые сценарием

Мы уверены, что для многих из вас самой интересной частью конструктора являются последние две строчки:

this.ajaxHelper = new net.ContentLoader( this, url, "POST", options.requestParameters || []

>;

Разумеется, мы знаем, что наш компонент требует возможностей Ajax. Благодаря везению и толике планирования мы уже имеем объект, выполняющий большую часть всей работы, выпадающей на долю Ajax, — речь идет об объекте net.ContentLoader, который мы предусмотрительно написали ранее. Объект DoubleCombo просто передает себя (посредством this) как параметр компонента вспомогательному объекту ContentLoader. Кроме того, как целевой URL запросов Ajax этому вспомогательному объекту передается параметр url, и с помощью строки "POST" задается метод HTTP-запроса.

Наконец, свойство requestParameters объекта опций (или пустой массив, если не была определена ни одна опция) передается как массив "постоянных" параметров, которые будут отправляться со всеми запросами Ajax. Напомним также, что поскольку мы передали this как аргумент компонента, объект DoubleCombo обязан реализовать указанный ранее неявный контракт с объектом net .ContentLoader. Другими словами, мы должны реализовать методы aj axUpdate () и handleError (). Чуть позже мы разберем этот момент подробно, а пока рассмотрим последнюю строку нашего конструктора:

this.initializeBehavior{);

Наконец-то наш конструктор делает что-то, похожее на желаемое поведение. Да, наступил момент, которого мы так долго ждали: реализация поведения. Все, что мы будем делать далее, непосредственно связано с обеспечением функциональных возможностей зависимых списков. Поэтому без лишних слов рассмотрим данный метод (а также все остальные методы DoubleCombo, которые нам потребуются). Благодаря тому что мы упорядочили всю инфраструктуру, наша задача уже не выглядит устрашающе. Помните, что все методы, которые встретятся при разборе данного примера, предполагаются внедренными в литеральный объект-прототип (точно так же, как мы делали при реализации net.ContentLoader).

DoubleCombo.prototype = { // все методы ... .

};

Ну что ж, заглянем под капюшон. Ниже приведен метод initializeBehavior().

initializeBehavior: function() { var oThis = this;

this.master.onchange = function() { oThis.masterComboChanged(); };

)/

Коротко и ясно. Данный метод помещает обработчик событий onchange в основной элемент select {ранее это делалось в самой разметке HTML). При срабатывании обработчик событий вызывает на нашем объекте masterComboChangedO другой метод.

masterComboChanged: function()

{

var query = this.master.options[

this.master.selectedlndex].value;

this.ajaxHelper.sendRequest(

'q=' + query );

Ь

 

Удивительно, но все по-прежнему коротко и ясно. Все, что требуется от этого метода, — это создать параметр запроса и отправить наш запрос Ajax. Поскольку вся работа Ajax была вынесена в другой объект, все действия формулируются с помощью одной строки кода. Напомним, что sendRequest {) создаст и отправит XMLHttpRequest, который направит отклик обратно нашему методу ajaxUpdate (). Запишем все сказанное.

ajaxUpdate: function(request) {

 

var slaveOptions = this.createOptions (

^/

request.responseXML.documentElement);

//Очистить существующие опции this.slave.length = 0;

//Заполнить новые опции

for ( var i = 0 ; i < slaveOptions.length ; i++ ) try{

this.slave.add(slaveOptions[i], null); }catch (e){

this.slave.add(slaveOptions[i] , -1) ;

}

ь

Данный метод принимает ответный XML-документ от объекта request и передает его методу createOptions(), который создает элементы option нашего зависимого элемента select. Затем метод просто очищает и повторно заполняет зависимый элемент select. Метод createOptions(), хотя я не является частью никакого публичного контракта, относится к вспомогательным методам, которые облегчают чтение и понимание кода. Его реализация и еще один вспомогательный метод, getElementContent (), показаны в листинге 9.13.

Листинг 9.13. Методы заполнения

createOptions: function (ajaxRespon.se) {

 

 

 

var newOptions = [];

 

 

 

var

entries

= ajaxResponse.getElementsByTagName('entry');

 

for

( var i — 0 ; i < entries.length ; i++

) {

 

 

var

text

= this.getElementContent(entries[i],

 

 

 

 

'optionText') ;

 

 

 

var

value

= this.getElementContent(entries[i],

 

 

 

 

'optionValue') ;

 

 

 

newOptions.push( new Option{ text, value

) );

 

}

 

 

 

 

 

 

return

newOptions;

 

 

ь

 

 

 

 

 

getElementContent: function(element,tagName)

{

 

 

var

childEleraent = element.getElementsByTagName(tagName)[0];

 

return

{childElement.text != undefined) ? childElement.text :

 

 

 

 

childElement.textContent;

Ь

 

 

 

 

m

 

Данные методы выполняют всю тяжелую работу, действительно извлекая значения из ответного документа XML и создавая по ним объекты опций. Напомним, что структура XML-отклика выглядит следующим образом:

<?xml version»'* 1.0" ?>

<selectChoice>

<e n t r y>

<o p t i o n T e x t > S e l e c t A T e r r i t o r y < / o p t i o n T e x t > <optionValue> - l</optionValue>

</entry>

<entry>

<o p t i o n T e x t > T e r r i t o r y D e s c r i p t i o n < / o p t i o n T e x t >

<o p t i o n V a l u e > T e r r i t o r y I D < / o p t i o n V a l u e > </entry>

</ s e l e c t C h o i c e >

Метод createOptions () последовательно проходит по всем элементам entry в XML-документе и извлекает текст из элементов optionText и optionValue посредством вспомогательного метода getElementContent (). Касательно метода getElementContent () стоит отметить только то, что он использует IE-совместимый атрибут text элемента XML, если он существует; в противном случае применяется стандартизованный W3C атрибут textContent.

Обработка ошибок

Ну вот и все. Вернее, почти все. Мы реализовали все линии поведения, необходимые для раскрытия потенциала данного компонента. Но постойте! Мы говорили, что обработка состояний ошибки также будет. Вы можете сказать, что нам требуется еще реализовать метод handleError (), чтобы с объектом net.ContentLoad.er можно было нормально работать. Ну так давайте реализуем его и действительно завершим эту задачу. Итак, какое восстанавливающее действие требуется при наличии ошибки? Пока что мы этого сказать не можем. Вообще-то, этот вопрос должно решать приложение, использующее

наш компонент DoubleComtoo. Похоже, что при такой формулировке нашлась работа нашему объекту опций (помните, мы передавали его конструктору?У Рассмотрим возможность такого контракта. Что будет, если мы создадим нащ компонент связных списков с кодом, подобным приведенному ниже?

function myApplicationErrorHandler(request)

{

//

Функция приложения, отвечающая

за обработку ошибок

}

 

 

 

var comboOptions = { requestParameters: [

 

 

"paraml=one", "param2=two" ],

errorHandler: myApplicationErrorHandler

 

};

 

 

 

v a r

doubleCombo • new DoubleCombo(

' r e g i o n ' ,

' t e r r i t o r y 1 ,

 

'DoubleComboXML.aspx',

 

 

comboOptions );

В этом сценарии, мы позволяем приложению определить функцию myApplicationErrorHandler (). Именно в реализацию данного метода мы можем поместить логику приложения, обрабатывающую ошибочные ситуации, Это может быть предупреждение. Или менее навязчивое сообщение "упс!" в стиле GMail. Суть такого решения заключается в том, что мы делегируем принятие решения приложению, использующему наш компонент. Как и ранее, мы предоставляем механизм и позволяем кому-то еще обеспечить семантику. Следовательно, теперь нам нужно написать метод handleError () объекта DoubleCombo.

handleError: function(request) { if ( this.options.errorHandler )

this.options.errorHandler(request);

}

Счастье компонента

Поздравляем! Наконец-то мы сделали все. У нас есть общий компонент, который мы можем создать с идентификаторами любых двух элементов select и некоторой информацией по конфигурации, также у нас есть возможность зависимого выбора. И тут... распахивается дверь1.

Пятница, 14:45, входит менеджер, ничего не смыслящий в программировании. "Джонсон, — восклицает он. — Нам нужна поддержка подтерриторий! ... И она должна быть готова к утру понедельника!" Драматическая пауза. "О-ох!" — выдавливаете вы из себя. Затем вы собираетесь и говорите: "Я сделаю это, сэр. Даже если мне придется проработать все выходные". Он вручает вам новый дизайн страницы:

<form>

<select id="region" name="region"><select> <select id="territory" name="territory"></select>

<select id="subTerritory" name="subTerritory"X/select> </form>

Начальство уходит. Вы открываете HTML-страницу в Emacs, поскольку так вам удобнее. Переходите сразу к заголовку. Курсор мигает — вы начинаете набирать.

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