Enhanced JavaServer Faces™ (JSF™) Components в IBM®Rational® Application Developer совместно с Standard JSF Components предлагают широкое многообразие функциональных возможностей высокого уровня, которые могут использоваться для быстрой разработки полнофункциональных и устойчивых Web-приложений. Библиотека не только обширна, но все ее компоненты сами по себе являются очень гибкими, готовыми к повторному использованию и, что вероятно самое важное, настраиваемыми. Такая гибкость присуща как компонентам структуры на стороне клиента, так и бизнес-логике на стороне сервера. Хорошим примером такого компонента является компонент Enhanced Faces Typeahead. Данная статья посвящена нескольким передовым приемам, демонстрирующим простоту настройки и максимально эффективное использование компонента Typeahead. Будут рассмотрены следующие темы:
- Создание Dynamic Filter на стороне сервера
- Изменение внешнего вида и поведения при помощи CSS и атрибутов тэгов
- Использование Typeahead-событий и JavaScript™ API
В данная статья предполагает, что вы имеете некоторый опыт работы с JSF-компонентами в Rational Application Developer. Пошаговые инструкции по созданию вашего первого компонента Typeahead приведены в ознакомительной статье, ссылка на которую указана в разделе "Ресурсы".
Реализация Dynamic Filter на стороне сервера
Самым общим примером использования компонента Typeahead является простой поиск в словаре. Например, при наборе строки появляется ниспадающий список часто используемых поисковых терминов, каждый из которых начинается с введенной пользователем строки (как показано на рисунке 1).
Рисунок 1. Простой Typeahead
Вот что происходит за кулисами:
- На стороне клиента сценарий (написанный на языке программирования JavaScript) регистрирует строку, введенную в поле ввода (происходит событие
keydown). - Объект
backchannelEnhanced Faces JavaScript Library передает поисковый термин на сервер в фоновом режиме.backchannelинкапсулирует всю функциональность и логику, необходимую для передачи XMLHttpRequest на сервер или для передачи информации через скрытый iFrame (в зависимости от обстоятельств). - На сервере формируется ответ в виде XML. Ответ (и запрос) можно увидеть в консоли любого поддерживающего JavaScript браузера, способного следить за трафиком
XMLHttpRequest. Обычный XML-ответ (два XML-фрагмента) выглядят примерно так, как показано в листинге 1:
Листинг 1. Typeahead XML-ответ сервера с 2 результатами<?xml version='1.0' encoding='ISO-8859-1'?> <taresponse> <suggestlist> <suggestion>bd0772-Ja</suggestion> <suggestion>ede651-Ja</suggestion> </suggestlist> </taresponse> <url>/test2/myPage.faces?$$ajaxid=form1:typeahead1&$$ajaxmode=axpartial</url>
- В этом сценарий есть функция обратного вызова
Callback, зарегистрированная сXMLHttpRequest. Она вызывается для передачи ответа в качестве параметра. Сценарий Typeahead затем создает список предложений в Document Object Model (DOM) путем вставки системы HTML-таблиц и тэгов div.
С точки зрения пользователя заполнение ниспадающего списка происходит незаметно и, что самое важное, не требует перезагрузки страницы. После заполнения списка можно использовать мышку или клавиатуру для выбора варианта из списка. Это завершит ввод информации в поле точно так же, как и при работе с любым другим элементом управления типа ниспадающего списка.
Часто необходимо реализовать более сложную логику, управляющую поиском на сервере. Ниже рассматривается пример, демонстрирующий применение фильтра в процессе поиска.
Структура данного примера показана на рисунке 2.
Рисунок 2. Структура примера компонента
Структура пакета компонента (typeaheadFilter.zip доступен в разделе "Загрузка") разделена, в сущности, на две части. С одной стороны компоненты структуры данных:
- Company (Компания)
- Customer (Клиент)
- Order (Заказ)
Как и ожидалось, шаблон знаком: компания со многими клиентами, у которых, в свою очередь, много заказов.
С другой стороны - логика поиска. Картой (Map), связанной с Typeahead, является компонент SimpleSearch, на который ссылается компонент SampleFilter. Компонент фильтрации содержит информацию о состоянии текущего фильтра. Поисковый компонент может обратиться за состоянием компонента фильтрации для определения результатов, возвращаемых в списке предложений. Компонент Root ссылается на обе стороны.
Компонент Root создает объект Company. Объект Company, в свою очередь, создает четыре объекта типа Customer:
- Wender's Washers
- Jackson's Juggernauts
- Pierre's Plumbing
- William's Web Development
Каждый объект Customer генерирует 30 объектов типа Order. Для каждого объекта Order создается строка идентификатора (ID String), содержащая шестнадцатеричное значение (до 0xf00000), за которым следует дефис и первые две буквы имени клиента (например, ede651-Ja или bd0772-Ja). Хотя эти числа не гарантируют абсолютной уникальности, они подходят для целей демонстрации в данной статье. Это коды, которые будет искать объект Typeahead.
Компонент, расширяющий тип java.util.AbstractMap (в данном примере, приведенном в листинге 2, это компонент SimpleSearch), инкапсулирует логику поиска для Typeahead. Typeahead вызывает метод get компонента Map. Метод get должен возвратить что-то одно:
- массив ArrayList, содержащий строки
- одну строку (может возвратиться в случае с единственным результатом)
-
null
Листинг 2. Логика поиска в компоненте SimpleSearch
public Object get(Object key) {
Customer[] customers =
filter.root.company.getCustomers();
ArrayList matches =
new ArrayList();
int len=0;
String s = (String)key;
System.out.println("in search, key: " + key);
for(int i=0; i<customers.length; i++)
{
// следующая строка отфильтровывает клиентов,
не соответствующих нашему фильтру
if (!customers[i].CustomerName.
equalsIgnoreCase(filter.getFilter())) continue;
System.out.println("using customer:
" + customers[i].CustomerName);
// если дойдем до этого места, то имеем:
Order[] orders = customers[i].getOrders();
for (int j=0; j<orders.length; j++){
String record = (String)
orders[j].getOrderID();
if (record.length() > s.length()) {
String subrecord =
record.substring(0,s.length());
if (subrecord
.equalsIgnoreCase(s)==true) {
System.out.println
("adding record to matches: " + record);
matches.add(len++,record);
}
}
}
}
return matches;
}
|
В листинге 2 обратите внимание на две строки, выделенные жирным шрифтом, которые меняют операцию поиска всех заказов на поиск заказов клиента, чье имя (Customer Name) соответствует filter String. То есть, если фильтр установлен в "Pierre's Plumbing", будут искаться только заказы данного пользователя. Это очень простой сценарий, но эту же методику можно было бы применить для, например:
- Получения всех заказов клиента за определенный период
- Фильтрации на основе некоторых переменных окружения
Создание простого демонстрационного приложения в Rational Application Developer
Приложив незначительные усилия, можно создать приложение в Rational Application Developer, которое демонстрирует описанную выше фильтрацию в работе. Для создания нового приложения и импорта компонентов выполните следующие действия.
- Создайте новый проект Dynamic Web Project с поддержкой Faces с указанием сервера вашей среды разработки, как показано на рисунке 3. Серверами, поддерживаемыми Typeahead, являются IBM® WebSphere® Application Server (V6.x или старше), IBM® WebSphere® Portal Server (V5.1 или старше) и Apache Tomcat V5.5.
Рисунок 3. Диалоговое окно New Dynamic Web Project
- Импортируйте компоненты в проект. Для этого нажмите правой кнопкой мыши на проект и выберите Import > Archive File, а затем перейдите в каталог, в котором сохранили файл typeaheadFilter.zip. Убедитесь в том, что выбрали вариант импорта файлов в исходную (по умолчанию "src") папку вашего проекта, как показано на рисунке 4.
Рисунок 4. Импорт пакета JavaBean
Теперь необходимо создать страницу по технологии JSP™ (JavaServer Pages™), а затем данные для нее.
- Создайте новую Web-страницу в папке WebContent вашего проекта, нажав правой кнопкой мыши на папку и выбрав New > Web Page, как показано на рисунке 5.
Рисунок 5. Создание новой JSP-страницы
Введите название страницы и нажмите кнопку Finish. - В окне Page Data View создайте новый компонент Faces Managed Bean. В разделе Class диалогового окна Add JavaBean введите
com.ibm.devworks.typeaheadfilter.Root, как показано на рисунке 6. Это можно быстро сделать с помощью кнопки справа. В поле Scope выберите вариантsession.
Рисунок 6. Добавление компонента Faces Managed Bean, "Root"
А сейчас необходимо добавить некоторые компоненты для ввода и вывода данных.
-
Перетяните поле
filterкласса filter из окна Page Data View в окно Page, как показано на рисунке 7. В диалоговом окне выберите Inputting data.
Рисунок 7. Добавление элементов управления для Filter
- Перетяните на страницу поле input из секции Enhanced Faces Components drawer (справа). Если секции drawer нет, возможно, нужно выбрать Unhide для нее в диалоговом окне Palette Customization. Это поле ввода будет вашим элементом управления Typeahead. Данный пример (рисунок 8) имеет таблицу и маленький фрагмент текста на странице для идентификации поля и точного его размещения.
- Хотя это и необязательно, вы можете перетянуть узел Company из вида Page Data на страницу для отображения содержимого Data во вложенной DataTable. Это может помочь визуализировать набор данных при исполнении во время разработки.
Рисунок 8. JSP в виде Design с элементами управления
На данном этапе все готово для разрешения функциональности Typeahead для поля ввода данных.
- В закладке Behavior (в виде Properties) для поля input разрешите функциональность Typeahead, отметив вариант Enable typeahead. Обратите внимание на то, что в виде Source есть тэг hx:inputHelperTypeahead, который был добавлен как потомок к у h:inputText.
- В закладке Design перетяните объект search из вида Page Data на вспомогательную кнопку Typeahead рядом с полем ввода, как показано на рисунке 9. Примечание: Если появится запрос на ввод типа для Map, выберите java.lang.Object.
Рисунок 9. Перетягивание объекта search для соединения с Typeahead
Теперь приложение готово для запуска на сервере и при выполнении должно выглядеть примерно так, как показано на рисунке 9. Вводя разные имена клиентов и нажимая Submit, вы можете заметить (рисунок 10), что набор результатов для Typeahead изменяется в соответствии с тем, для какого Customer установлен фильтр. Вы можете собрать это приложение за несколько минут.
Если потратить еще немного времени, можно разработать сложный фильтр и системы опережающего ввода, используя эту методику. Следующим логическим шагом для этого приложения могло бы быть разрешение функциональности typeahead также и для поля Filter (ввод Customer). Это могло бы быть полезно при наличии большого числа клиентов.
Рисунок 10. Выполнение приложения на сервере (обратите внимание на то, что возвращаются только заказы для Wender's Washer)
Этот пример использует технологию JavaBeans, поскольку она хорошо работает в качестве простой демонстрации. Однако, легко можно использовать в качестве хранилища данных любой сервер. Это довольно мощный подход. Например, можно разработать SQL-запросы для задачи вручную и использовать JDBC-посредник (Java™ Database Connectivity API) для обращения к базе данных.
Событиями для Typeahead являются inProgress, oncomplete, onstart и onerror. Обработка событий указывается в виде Quick Edit для Typeahead. Каждое из них можно связать с JavaScript-функцией. Пока сконцентрируемся на добавлении JavaScript-обработчика события oncomplete.
Необходимо связать функцию с тэгом. Это можно сделать в виде Quick Edit, выполнив следующие действия.
- Выберите oncomplete в левой стороне вида.
-
В правой стороне введите предупреждение (
You are in the oncomplete event handler) (Вы находитесь в обработчике события oncomplete).
Код в соответствии с листингом 4 должен сгенерироваться в виде Source страницы.
Листинг 4. Простой обработчик события
<script type="text/javascript">
function func_1(thisObj, thisEvent) {
//используйте 'thisObj' для прямой ссылки на этот компонент
//вместо ключевого слова 'this'
//используйте 'thisEvent' для ссылки на сгенерированное событие
//вместо ключевого слова 'event'
alert("You are in the oncomplete event handler");
}
</script>
.
.
.
<h:inputText id="text1" styleClass="inputText">
<hx:inputHelperTypeahead id="typeahead1" styleClass="inputText_Typeahead"
value="#{myRoot.filter.search}" oncomplete="return func_1(this, event);">
</hx:inputHelperTypeahead>
</h:inputText>
|
Можно быстро проверить, как это работает, запустив страницу на сервере. Когда клиент получает список предложений, отображается всплывающее окно сообщений. Упомянутый в сгенерированном комментарии объект thisObj обращается к полю DOM input, с которым связан Typeahead. Это может быть полезно для получения его размеров, содержимого, id и т.д.
Однако, что может быть еще полезнее, так это извлечение JavaScript-объекта Typeahead Behavior. Behavior - это концепция в JavaScript-библиотеке Extended Faces, в которой тип функциональности может быть ассоциирован с DOM-объектом, событием и типом Behavior. В данном случае DOM-объектом является поле ввода, событием является onkeydown, а типом поведения является typeahead, что выражается в коде, приведенном в листинге 5.
Листинг 5. Извлечение объекта Typeahead behavior
var myTypeahead = hX_5.getBehaviorById(thisObj.id,"typeahead","onkeydown");
|
Наличие доступа к этому объекту позволяет извлекать строку предложения по индексу. В листинге 6 показан пример обработчика, который выполняет итерацию по предложениям, добавляет их к String, а затем отображает пользователю (рисунок 11).
Листинг 6. Обработчик, получающий и отображающий список предложений из Typeahead
<script type="text/javascript">
function func_1(thisObj, thisEvent) {
var myTypeahead = hX_5.
getBehaviorById(thisObj.id,"typeahead","onkeydown");
var i, s = "The suggestions are: \n";
for(i=0; i<myTypeahead
.suggestionCells.length; i++)
{
s += (i+1) + ". " + my
Typeahead.getSuggestion(i) + "\n";
}
alert(s);
}
</script>
|
Рисунок 11. Доступ к предложениям в Event
Что еще можно сделать - получить текст, введенный в поле ввода. После возврата списка предложений первая строка вставляется в поле ввода. Если результат не равен уже набранной строке, текст поля ввода меняется на первое предложение, выделяя часть предложения, добавленного к тексту поля ввода во время процесса автозаполнения. Объект Typeahead сохраняет длину введенной пользователем строки в переменной typedLength. Следовательно, можно легко получить введенную строку (из Typeahead), используя код, приведенный в листинге 7.
Листинг 7. Получение введенной пользователем строки из Typeahead Behavior
var myTypeahead = hX_5.getBehaviorById(thisObj.id,"typeahead","onkeydown");
var typedSoFar = thisObj.value.substring(0,myTypeahead.typedLength);
|
Изменение внешнего вида и поведения
Одним из огромных преимуществ компонентов Enhanced JSF Components, спакетированных с Rational Application Developer, является их высокий уровень настраиваемости. В данном разделе вы узнаете, как воспользоваться преимуществом этой настраиваемости через CSS и атрибуты тэгов, используя инструментальные средства Rational Application Developer. Далее в статье будут рассмотрены три различных контекста для Typeahead, а также будет продемонстрировано, как можно изменить его поведение и внешний вид, исходя из каждого контекста. Приводятся фрагменты исходного JSP-кода и соответствующие CSS-стили (если есть).
Ненавязчивый ниспадающий список
Часто ниспадающие списки в GUI (графический интерфейс пользователя) Web-приложения могут выглядеть навязчивыми. Это действительно так в ситуациях, когда их использование маловероятно. Во многих таких сценариях существует не так уж много вариантов, где помощь Typeahead могла бы пригодиться. Например, представьте ситуацию, когда вы вводите заданное имя. Как правило, это не представляет проблемы, поскольку они, обычно, не больше восьми символов. Когда вы не уверены в орфографии, можно много выиграть от использования поддержки Typeahead. С ней можно просто ввести только первые несколько букв имени и выбрать его из списка. Еще одним похожим сценарием является ситуация, когда Web-разработчик не хочет скрывать информацию на странице, в частности, когда ввод пользователя зависит от способности просматривать содержимое всей страницы.
Одним из способов сделать Typeahead менее навязчивым является уменьшение размера ниспадающего списка. Этого можно добиться двумя способами. Первый - уменьшить количество строк, появляющихся в ниспадающем списке, что легко сделать в закладке Properties для hx:inputHelperTypeahead, введя число в поле Height of Suggestion Area. Вы увидите, что здесь также есть поле Maximum number of suggestions displayed (Максимальное количество отображаемых предложений) (рисунок 12). Оно используется только для ограничения количества строк, возвращаемых сервером.
Код, передающий результаты в виде XML-списка (листинг 8), будет включать в себя только до n результатов, где n - это возвращаемый максимум. Если высота списка предложений установлена меньшей возвращаемого количества результатов, в ниспадающий список будет автоматически включаться полоса вертикальной прокрутки. При этом нет необходимости уменьшать размер возвращаемого набора для уменьшения избыточности информации на экране.
Рисунок 12. Ограничение высоты ниспадающего списка
Листинг 8. Атрибуты размера результатов
<h:inputText id="text1" styleClass="inputText">
<hx:inputHelperTypeahead id="typeahead1" styleClass="inputText_Typeahead"
value="#{myRoot.filter.search}" oncomplete="return func_1(this, event);"
size="2" maxSuggestions="100"></hx:inputHelperTypeahead>
</h:inputText>
|
Вторым вариантом является полный пропуск ниспадающего списка Typeahead путем активизации режима автозаполнения. Когда активен режим автозаполнения, на странице не отображается ниспадающий список, но ввод информации завершается автоматически до первого результата из набора, возвращаемого с сервера. Таким образом, элемент управления Typeahead не использует какие-либо дополнительные участки экрана. Это может быть полезно в случаях, когда возвращаемый результат невелик по размерам, и не часто нужно выбирать более чем из одного варианта.
Например, представьте ситуации, когда вводится имя из списка контактов. Обычно ввод имени и первых нескольких букв фамилии могут однозначно указать результат. Кроме того, функциональность выбора из списка результатов не удаляется, поскольку вы все равно можете просмотреть список при помощи клавиш управления курсором на клавиатуре.
Третий способ уменьшить навязчивость - использовать прозрачность в ниспадающем списке. Если ниспадающий список прозрачен, ваше внимание не отвлекается от поля ввода при работе с ним. Если прозрачность достаточна, пользователи все равно способны видеть информацию на странице и, при необходимости, обращаться к ней при заполнении поля ввода. На рисунке 13 показан пример такого подхода.
Рисунок 13. Прозрачный Typeahead-список
В листинге 9 показан код CSS для трех Typeahead-классов, использующихся для формирования эффекта прозрачности в области ниспадающего списка. Имеющие отношение к прозрачности атрибуты выделены жирным шрифтом.
Листинг 9. Прозрачность в CSS
.inputText_TypeAhead {
background-color: #ccddff;
border-width: 1px;
border-style: solid;
border-color: ThreeDDarkShadow;
width:300px;
-moz-opacity: .7;
opacity: .7
}
.inputText_TypeAhead-List {
background-color: transparent;
text-align: left;
vertical-align: middle;
height: auto;
font-family: sans-serif;
font-weight: 400;
font-size: 10pt;
border-collapse: collapse
}
.inputText_TypeAhead-Item {
background-color: transparent;
color: WindowText;
padding-left: 1pt;
padding-right: 1pt
}
|
Отображение длинных предложений при помощи длинного списка предложений
Часто неудобно вводить длинные строки в поле ввода (например, при вводе длинных химических составных названий или адресов). Здесь Typeahead может помочь, предложив список вариантов после ввода фрагмента полной строки. Это очень эффективно в ситуациях, когда набор данных со средней длиной строк в 150 символов различается, в основном, только по первым 10 символам.
Для достижения указанного эффекта необходимо изменить стиль какого-нибудь Typeahead. Хотя Typeahead может обработать свернутый текст, это нежелательно, особенно когда сворачивание производится более одного раза. Вместо этого можно увеличить ширину области предложений через CSS. По умолчанию ширина этой области установлена в ширину поля ввода. И хотя можно сделать поле ввода удлиненным для длинных строк, это не годится для четкого и компактного дизайна. Способ избежать ограничения ниспадающего списка до ширины поля ввода - снять отметку с варианта Match width of the suggestion area with the input field (Соответствие ширины области предложений ширине поля ввода) в виде hx:inputHelperTypeahead Properties. Это установит значение matchWidth=false в тэге Typeahead.
Рисунок 14. Флажок Matchwidth
Затем необходимо указать ширину в CSS, что приведет к GUI, аналогичному изображенному на рисунке 15. Для указания ширины можно использовать любую приемлемую в CSS метрику. В данном примере используется 400px. Требуется изменить только атрибут width в базовом CSS-классе, как показано в листинге 10.
Листинг 10. Увеличенная ширина в CSS
.inputText_TypeAhead {
background-color: #ccddff;
border-width: 1px;
border-style: solid;
border-color: ThreeDDarkShadow;
width: 400px;
-moz-opacity: .7;
opacity: .7
}
}
|
Рисунок 15. Широкий ниспадающий список
Теперь вы можете свободно управлять шириной Typeahead либо в абсолютных единицах (указывая точную ширину), либо относительно содержащего его элемента (указывая проценты).
Трудности возникают при попытке ввода экспромтом незнакомых строк, либо трудных строк, например, алфавитно-цифровых. Примерами таких строк могли бы быть номера деталей или идентификационные номера. В таких случаях лучше активизировать запрос списка предложений в процессе ввода пораньше. В виде Properties есть два поля, контролирующих время активизации Ajax-запроса списка предложений:
- В одном указывается количество символов, после ввода которых активизируется запрос.
- В другом указывается время, ожидаемое после последнего пользовательского события нажатия клавиши.
По умолчанию атрибуты startCharacters и startDelay (показаны на рисунке 16) установлены в один символ и 500 миллисекунд соответственно. В ситуации с незнакомыми строками, наверное, лучше уменьшить временную задержку до, скажем, 100ms.
Рисунок 16. Атрибуты задержки
Однако, как и повсюду в Ajax, это может значительно увеличить трафик на сервере. Рекомендуется эффективно комбинировать эти два атрибута для оптимизации соотношения производительности и удобства использования функциональности Typeahead. По этой же причине при работе с хорошо знакомыми строками имеет смысл увеличить эти атрибуты.
| Описание | Имя | Размер | Метод загрузки |
|---|---|---|---|
| Пример для сценария фильтрации Typeahead | typeaheadFilter.zip | 4 KB | HTTP |
Научиться
- Оригинал статьи "Advanced usage of the Typeahead control in Rational Application Developer V7.0".
-
"Улучшение удобства использования Web-приложений, в которых применяются поля с опережением ввода, при помощи JSF, AJAX и Web-службы в Rational Application Developer V7" (developerWorks) - пошаговое введение в основы элемента управления Typeahead.
-
Посетите страницу продукта Rational Application Developer для расширения опыта его использования.
-
Семинары и обучение на IBM developerWorks Россия.
Получить продукты и технологии
-
Загрузите свободно распространяемую пробную версию Rational Application Developer V7.0.
- Разработайте ваш следующий проект, используя пробное программное обеспечение IBM, доступное для загрузки непосредственно с developerWorks.
Обсудить
- Примите участие в обсуждении материала на форуме.
-
Форум WebSphere : Задайте вопросы о WebSphere JavaServer Faces (JSF).

Адам Бермингем (Adam Bermingham) работает инженером-программистом в IBM Dublin Software Lab с октября 2004. Получил степень бакалавра по вычислительной технике в Trinity College, Dublin. Имеет сертификат Java-программиста от Sun. Занимается разработкой Enhanced JavaServer Faces Components IBM и, с недавних пор, IBM LanguageWare.