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

Глава 10. Опережающий ввод АЛЛ

Мы можем предложить более продуманное решение этой проблемы, введя в специальный объект дополнительный параметр и отправив его на сервер. Есть и другая возможность — работать с тем, что мы имеем, и внести в код минимальные изменения. В таком случае простое решение включает изменение одной строки кода и добавление оператора if в код серверной части сценария.

Итак, нам требуется каким-то образом научиться различать элементы на сервере, определяя, какой элемент потребовал дообработки. Сообщить о различии проще всего с помощью имени, содержащегося в элементе. В данном случае мы ссылаемся на имя текстового окна. Для внедрения описанной возможности мы изменили строку параметров так, как показано в листинге 10.20.

Листинг 10.20. Измененная функция TypeAhead() function TypeAhead(xStrText){

 

var strParams = "q=" + xStrText + "&where=" +

 

theTextBox.obj.matchAnywhere + "&name=" + theTextBox.name;

 

var loaderl = new net.ContentLoader(theTextBox.obj.serverCode,

_}

BuildChoices,null,"POST",strParams);

m

 

 

Немного изменив переменную strParams в функции TypeAhead(), в параметрах формы, отправляемых на сервер, мы теперь передаем имя текстового окна. Это означает, что мы можем обращаться к этому значению на сервере и использовать оператор if-else либо case для запуска другого запроса. В таком случае нам не требуется реализовывать несколько страниц, соответствующих различным элементам.

10.5. Реструктуризация

Разработав достаточно сильный набор функций, обеспечивающих возможности опережающего ввода, мы можем подумать, как реструктуризировать все эти возможности в более удобном для использования виде. То, что мы создали на текущий момент, обеспечивает функциональные возможности, необходимые для предоставления на выбор ряда вариантов. Однако данная структура имеет свои недостатки с точки зрения работы, требуемой от разработчика при внедрении ее на Web-страницу (или на 20-30 Web-страниц).

Давайте на минуту представим, что перед нами, как перед главным архитектором использующей Ajax Web-структуры, поставлена задача написать компонент опережающего ввода, который будет использовать вся компания. После завершения вводного собрания мы получаем краткий перечень функциональных требований. Не совсем еще понимая суть дела, мы просматриваем этот документ (табл. 10.2).

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

412

Часть IV. Ajax в примерах

 

Таблица 10.2. Наши функциональные требования

 

Номер Описание требования

Приоритет

1

Компонент должен работать с существующей разметкой HTML, не

1

 

требуя никакого ее изменения. Допускается лишь простая модификация

 

 

заголовка с целью внедрения линии поведения компонента

 

2

Компонент должен без дополнительных усилий поддерживать

1

 

многократное использование на одной странице

 

3Должна существовать возможность индивидуальной настройки каждого 1 экземпляра компонента Под этим имеются в виду как аспекты поведения (например, учет регистра, поиск с любого места), так и стилевое оформление CSS

4

Компонент не должен вводить глобальные переменные. Компания

1

 

пользуется сторонними библиотеками JavaScript, и глобальное

 

 

пространство имен уже достаточно засорено. Использование любых

 

 

глобальных имен (кроме названия самого компонента) строго

 

 

запрещено

 

5

Компонент должен предоставлять разумные значения по умолчанию

1

 

для всех конфигурационных опций

 

6

Компонент должен работать в Internet Explorer и Firefox

1

7

Для уменьшения работ по кодированию, требуемых для улучшения

1

 

качества и надежности решения, компонент должен быть создан на

 

 

основе структуры с открытым исходным кодом

 

8

Кстати, если получится, сделайте это до конца недели

1

вания. Несмотря на то что мы проделали немалую работу, имеющийся сценарий не удовлетворяет даже половине из них. Сценарий уже готов, поэтому о требовании 7 можно забыть: нам не нужно уменьшать объем работ. Очевидно, что требованию 8 сценарий также удовлетворяет (по той же причине). Он поддерживает различные браузеры, поэтому снимаем и требование 6. А вот что касается остального, то определенную работу проделать все же придется. У нас есть всего неделя, поэтому начнем.

10.5.1. День 1: план разработки компонента TextSuggest

Прежде всего нам нужно определиться с тем, как поднять производительность и уложиться в отведенное время. Один из лучших способов — переложить работу на другихЕсли кто-то может сделать часть работы, пусть он ее для нас сделает. В данном случае мы собираемся воспользоваться библиотекой с открытым исходным кодом Rico (http: //openrico. org) и расширением Prototype.js (http://prototype.conio.net/). Библиотека Rico предлагает некоторую инфраструктуру Ajax, эффекты и вспомогательные методы, которые повысят скорость нашей разработки. Prototype предлагает инфраструктуру Для прекрасных синтаксических идиом, благодаря которым наш код будет выглядеть понятнее и потребует меньше времени на разработку. Поэтому давайте внимательно изучим последствия использования Prototype и Rico.

Глава 10 Опережающий ввод 413

prototype

prototype предлагает разработчикам несколько расширений основного объекта JavaScript, а также несколько функций, способствующих поддержанию хорошего стиля кодирования. Ряд из них мы используем в нашем примере.

Объект Class

Объект Class, представленный в библиотеке Prototype, имеет единственный метод create (), отвечающий за создание экземпляров, которые могут иметь любое число методов. Метод create {) возвращает функцию, вызывающую другой метод того же объекта — i n i t i a l i z e (). Звучит немного сложно, но на практике все просто По сути, таким образом формируется синтаксическая основа для задания типов в JavaScript. Идиома выглядит следующим образом:

var TextSuggest = Class.create(); TextSuggest.prototype = {

// Вызывается в процессе создания

initialize: function( pi, p2, p3 ) {

h

};

В данном фрагменте кода создается то, что можно считать "классом" (хотя сам язык не поддерживает такой концепции), и определяется функцияконструктор i n i t i a l i z e (). Клиент компонента может создавать экземпляры класса с помощью приведенной ниже строки кода.

var textSuggest = new TextSuggest(pi, p2, p3);

Метод extend О

Библиотека Prototype расширяет базовый объект JavaScript и добавляет метод, именуемый extend(), открывая этот метод для всех объектов. Метод extend () принимает в качестве параметров два объекта: базовый объект и объект, который будет его расширять. Свойства расширяющего объекта переносятся на базовый объект. Это позволяет использовать механизм расширения объектов на уровне экземпляров. Этой возможностью мы воспользуемся позже, когда будем реализовывать для настраиваемых параметров компонента TextSuggest значения по умолчанию.

Метод bind/bindAsEventListener()

Библиотека Prototype также добавляет два метода к объекту Function: bind() и bindAsEventListener(). Эти методы предлагают синтаксически элегантный способ создания замыкания функций. Напомним, как мы создавали замыкания в других примерах:

oThis • this;

this.onclick = function() { oThis.callSomeMethod() };

С помощью метода bind() библиотеки Prototype того же результата можно добиться гораздо проще.

this.onclick = this . callSomeMethod.binci( this);

API bindAsEventHandler() передает методу объект Event и сглаживает различия между Internet Explorer и стандартизованной W3C моделью событий!

414 Часть IV. Ajax в примерах

Метод $ — синтаксическая "конфетка"

В JavaScript в названиях методов можно использовать определенные специальные символы, например знак доллара ($). В библиотеке Prototype этот малоизвестный факт используется для инкапсуляции в метод одной из наиболее распространенных задач в DHTML-программировании: извлечения элемента из документа на основе его идентификатора. Таким образом, в нашем коде мы сможем писать конструкции следующего типа:

$('textField').value = aNewValue;

При этом мы обходимся без указанных ниже громоздких структур.

var textField = document.getElementByldf'textField1 ) textField.value = aNewValue;

Rico

Используя Rico, мы получаем Prototype бесплатно. Посмотрим, что нам требуется от Rico. Rico предлагает богатый набор линий поведения, возможностей перетаскивания и кинематических эффектов, но поскольку мы пишем компонент, использующий единственное текстовое поле, то большинство из доступных возможностей нам не понадобится. Однако еще есть прекрасный обработчик Ajax и некоторые вспомогательные методы, предлагаемые Rico. Вспомогательные методы Rico мы рассмотрим по ходу разбора примера, а сейчас остановимся на предлагаемой Rico инфраструктуре Ajax. Возможности Ajax Rico публикуются посредством единственного объекта, доступного для документа ajaxEngine. API ajaxEngine предоставляет поддержку для регистрации логических имен для запросов и регистрации объектов, знающих, как обрабатывать ответы Ajax. Рассмотрим, например, следующий код:

ajaxEngine.registerRequest( 'getlnvoiceData', 'someLongGnarlyUrl.do' ) ;

ajaxEngine.registerAjaxObject( 'xyz', someObject );

В первой строке кода регистрируется логическое имя потенциально громоздкого URL Ajax. Далее при отправке запросов можно использовать это логическое имя, не отслеживая упомянутый громоздкий URL. Пример такого использования приведен ниже.

ajaxEngine.sendRequest('getlnvoiceData', request parameters . . . );

Метод registerRequest () локализует использование указателей URL — теперь они встречаются только в одном месте, обычно это обработчик событий onload в разделе тела. Если URL требуется изменить, это можно сделать в месте регистрации, не затрагивая остальную часть кода-

Метод registerAjaxObject() иллюстрирует регистрацию объекта обработки Ajax. В предыдущем примере подразумевалось, что в ответах необходимо обращаться к объектной ссылке someObject с помощью логического имени xyz, причем эта ссылка необходима для обработки ответов Ajax посредством метода ajaxUpdate ().

Исходя из того, что мы используем описанные функциональные возможности объекта ajaxEngine, нам осталось только рассмотреть ответный XML-

Глава 10 Опережающий ввод 415

документ, ожидаемый процессором Ajax. Этот документ немного отличается от динамически генерируемого сценария JavaScript, который возвращался в предыдущей версии данного примера, но Rico ожидает получить XML. Все элементы <response> документа должны находиться внутри элемента верхнего уровня <ajax-response>. Внутри указанного элемента сервер может возвращать столько элементов <response>, сколько требует приложение. Такая возможность очень удобна, поскольку позволяет серверу возвращать ответы, обрабатываемые различными объектами, обновляющими потенциально несвязанные области Web-страницы, — например, для обновления области состояния, области данных и конечной области вывода. XML-документ для предыдущего примера приведен ниже.

<аj ax-response>

<response type="object" id="xyz">

. . . the r e s t of the XML response as normal . . .

</response> <response ... >

more response elements if needed.. </response>

</ajax-response>

Данный XML-документ указывает ajaxEngine, что данный запрос должен обработать объект, зарегистрированный с идентификатором xyz. Процессор Ajax находит объект, зарегистрированный с именем xyz, и передает содержимое соответствующего элемента <response> методу ajaxUpdate().

Вот и все. День получился довольно коротким. Некоторое время мы потратили на изучение структур с открытым исходным кодом, на которые мы можем опираться, и разработали план внедрения их в требуемый компонент. Мы еще не написали ни строчки кода, но решили, как ускорить дальнейшую работу над проектом. Кроме того, мы подобрали платформу, которая поможет повысить производительность работы (удовлетворив при этом требованию 7 технического задания). Кодирование начнется завтра.

10.5.2.День 2: создание TextSuggest — понятного

инастраиваемого компонента

Теперь, когда у нас есть хорошая технологическая платформа, мы можем создавать на ней свой компонент. При работе над проектами часто удобно идти от желаемого результата, заранее думая о контракте нашего компонента. Напомним наше первое требование.

Требование 1. Компонент должен работать с существующей разметкой HTML, не требуя никакого ее изменения. Допускается лишь простая модификация заголовка для внедрения линии поведения компонента.

Из-за этого требования мы оставляем нетронутым практически все, что находится внутри элемента <body>. Поэтому предположим, что нам требуется ввести сценарий в HTML посредством HTML-кода, подобного приведенному в листинге 10.21.

416 Часть IV. Ajax в примерах

Листинг 10.21. HTML-разметка компонента TextSuggest

<html>

 

 

 

<head>

 

 

 

<script>

 

 

 

v a r s u g g e s t O p t i o n s = { / * d e t a i l s

to

come*/ };

f u n c t i o n

i n j e c t S u g g e s t B e h a v i o r ( ) {

//

Создать компонент в <head>

sugges t

= new TextSuggest(

 

 

1 f i e l d l ' , ' t y p e A h e a d D a t a . a s p x ' , s u g g e s t O p t i o n s ) ;

}>;

</ s c r i p t > </head>

<body o n l o a d = " i n j e c t S u g g e s t B e h a v i o r ( ) " > <form name="Forml">

AutoComplete Text Box:

< i n p u t t y p e = " t e x t " id—"fieldl" n a m e = " t x t U s e r I n p u t " > _ - </form>

</body>

</html>

Смысл приведенного HTML-кода заключается в том, что нам требуется создать объект с идентификатором текстового поля, к которому мы будем привязаны, указателем URL источника данных Ajax и набором конфигурационных объектов, которые еще будут заданы. (Чтобы это сработало, текстовое поле должно иметь идентификатор.) Все, что находится внутри элемента <body>, остается без изменений. Разобравшись с этим, займемся конструктором. Имя создаваемого компонента TextSuggest помещается в глобальное пространство имен с помощью функции-конструктора, которая (как вы помните) генерируется методом Clas s. create {) библиотеки Prototype, как показано в листинге 10.22.

Листинг 10.22. Конструктор TextSuggest

TextSuggest = Class.create(); TextSuggest.prototype = { initialize:

function{anld, url,

options) {//О Ссылка на входной элемент this.id = anld;

this.textlnput = $(this.id);

var browser = navigator.userAgent.toLowerCase();

//© Детектировать тип браузера this.isIE =

browser.indexOff"msie") != -1; this-isOpera =

browser.indexOf{"opera")!= -1; // © Установить значения по умолчанию

this.suggestions = []; this.setOptions(options); this.initAjax(url);

Ьthis.inj ectSuggestBehavior();

}±11

 

 

 

Глава 10. Опережающий ввод 417

Разберем этот конструктор. Как упоминалось ранее, конструктору передается идентификатор текстового ввода, с которым нужно связать предложение вариантов. Для поля ввода хранится ссылка как на идентификатор, так л на элемент DOM О. Далее определяется браузер пользователя и записывается состояние, которое понадобится компоненту позже, когда потребуется информация о среде времени выполнения этого браузера ©. В данном случае специальный код требуется только для Internet Explorer и Opera, поэтому проверяется использование только этих браузеров.

Настройка Ajax и введение в код нужной линии поведения будет рассмотрен позже ©. Пока же (до конца дня) сосредоточимся на возможности настройки компонентов. Как вы помните, ранее была создана функция Setproperties (), содержащая все настраиваемые аспекты нашего сценария.

function SetProperties

(xElem, xHidden, xserverCode, xignoreCase, xmatchAnywhere, xmatchTextBoxWidth, xshowNoMatchMessage, xnoMatchingDataMessage, xuseTimeout, xtheVisibleTime){

}

Написанный код удовлетворяет требованию, которое касается возможности настройки, но не обеспечивает удобный API или подходящие значения по умолчанию. Для решения этих проблем вводится объект опций, который передается конструктору. Объект опций содержит свойство для каждого конфигурационного параметра компонента опережающего ввода. Теперь нужно заполнить опции конфигурационными параметрами.

var suggestOptiona - { matchAnywhere : true, ignoreCase : true }; function injectSuggestBehavior() {

suggest = new TextSuggest('fieldl', ' typeAheadXML.aspx', suggestOptions ); } ) ;

Данная простая идиома предлагается следующие дополнительные преимущества.

Благодаря ей сигнатура конструктора выглядит понятнее. Сигнатура клиентских страниц, на которых используется наш компонент, может создаваться всего с тремя параметрами.

Можно вводить дополнительные конфигурационные параметры, не меняя контракт конструктора.

Можно написать изящную функцию setoptions(), предоставляющую значения по умолчанию для всех незаданных свойств, и позволить пользователю задавать только те свойства, которые он желает изменить.

Впоследнем пункте описано именно то, что делает метод setOptions (), включенный в конструктор ранее ©. Разберем его.

setOptions: function(options) {

t h i s , o p t i o n s • { suggestDivClassName: ' s u g g e s t D i v ' ,

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