
Крейн Д., Паскарелло Э., Джеймс Д,. AJAX в действии / Ajax в действии / ajinact / Ajax в действии
.pdf
Глава 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.

420 Часть IV. Ajax в примерах
Во внутреннем представлении запросы отправляются следующим образом; Щ aj axEngine.sendRequest{
'fieldl^request',lparaml=vall1, 'param2=val2', . . .
);
Учитывая вышесказанное, для получения Ajax-модификации нашего компонента необходимо отправить запрос и обработать ответный документ. Посмотрим, как это можно сделать.
Предлагаемый текст — отправка запроса Ajax
Разумеется, до отправки запроса придется сделать кое-что еще. Текстовый ввод должен сгенерировать событие onchange, которое мы будем ждать, и при выполнении определенных условий отправлять запрос о предполагаемых вариантах. Пока что написанные нами фрагменты кода находятся не на своих местах, но мы это поправим. Мы по-прежнему можем оперировать терминами контрактов и ответственности метода, которые желательно независимо реализовать в конечном продукте. Поэтому предположим, что некоторый фрагмент кода (который мы еще напишем) решает, что необходимо отправить запрос и получить ряд предполагаемых вариантов. Назовем эту функцию
sendRequestForSuggestions () |
и реализуем ее следующим образом: |
|
sendRequestForSuggestions; |
function() { |
|
if ( this.handlingRequest ) { |
||
this.pendingRequest |
= |
true; return; |
} |
|
|
this.handlingRequest |
= true; |
this.callRicoAjaxEngine();
Ь
Единственное, что делает приведенный код, — это вызывает th.is.callRicoAj axEngine () при условии, что запрос не обработан. Этот простой механизм присваивает внутреннему булеву свойству this.handlingRequest значение true после создания запроса Ajax и false (рассмотрено ниже) — после обработки запроса. Если метод вызывается в момент обработки запроса, булеву свойству this.pendingRequest присваивается значение true. Это состояние сообщает обработчику, что, возможно, ему придется отправить еще один запрос после обработки текущего. Далее мы рассмотрим метод callRicoAj axEngine {), приведенный в листинге 10.23.
Листинг 10.23. Использование a j axEngine (Rico)
callRicoAjaxEngine: function() { // Построить массив параметров
var callParms = [];
callParms.push( this.id + '^request1); callParms.push{ T id= < + this.id); callParms.push( 'count=' + this.options.count);
callParms-push( 'query=' + this.lastRequestString); callParms.push( 'match_anywhere=l + this.options.matchAnywhere); callParms.push( 'ignore_case=' + this.options.ignoreCase);
var additionalParms = this.options.requestParameters t{ []; for( var i=0 ; i < additionalParms.length ; i++ )
callParms.push{additionalParms[i]);