Содержание


JSF 2 fu

Добавление поддержки Ajax в готовые составные компоненты

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

Comments

Серия контента:

Этот контент является частью # из серии # статей: JSF 2 fu

Следите за выходом новых статей этой серии.

Этот контент является частью серии:JSF 2 fu

Следите за выходом новых статей этой серии.

В предыдущей статье серии JSF 2 fu мы обсудили основные аспекты реализации компонента автозаполнения для текстового поля со встроенной поддержкой Ajax. Разработчики веб-страниц используют подобные компоненты в facelet, и в этом случае сам компонент отвечает за организацию Ajax-вызовов. Использование встроенной поддержки Ajax, безусловно, удобно, но не менее удобный подход состоит в том, чтобы предоставить разработчикам страниц возможность самим добавить поддержку Ajax в уже готовый (может быть, уже давно готовый) составной компонент. В этой статье мы научимся использовать поддержку Ajax в готовых компонентах.

Как вы могли убедиться, прочитав статью JSF 2 fu: Часть 3. Обработка событий, JavaScript и Ajax, тег JSF 2 <f:ajax> позволяет добавить поддержку Ajax к встроенным компонентам JSF 2. Так, например, с помощью этого тега вы легко можете превратить обычную кнопку submit в Ajax-кнопку:

<h:commandButton value="Click me">
  <f:ajax>
</h:commandButton>

Однако для того, чтобы JSF 2 тег <f:ajax> заработал в составных компонентах, потребуется немного убеждения, поскольку эти составные объекты являются всего лишь контейнерами компонентов.

Так, например, в статье JSF 2 fu: Часть 2. Шаблоны и составные компоненты я создал составной компонент пиктограммы icon, который представляет собой ссылку в виде графического изображения. Когда пользователь щелкает мышкой на пиктограмме, компонент link передает соответствующую форму обработчику событий компонента link на стороне сервера. Использовать такую пиктограмму очень просто:

<util:icon image="...">
  <f:actionListener for="link" type="...">
</util:icon>

Раз вы можете с помощью тега <f:ajax> превратить кнопку submit в кнопку Ajax, то логично предположить, что то же самое можно проделать и с пиктограммой:

<util:icon image="...">
  <f:ajax>
  <f:actionListener for="link" type="...">
</util:icon>

Однако приведенный выше фрагмент кода не будет работать, поскольку я применил тег <f:ajax> к компоненту icon, в то время как должен был применить его к компоненту link внутри компонента icon.

Для того чтобы мой пример заработал, требуется механизм, который позволит мне привязать Ajax-вызовы к компоненту link внутри компонента icon, или, в более общем случае, механизм, который позволит привязать функциональность Ajax к компоненту, который находится внутри другого составного компонента. Изучение подобного механизма (он реализован в Mojarra и Apache MyFaces, но не документирован в JSF 2.0) и является основной целью данной публикации (следует отметить, что поддержка такого механизма была добавлена в MyFaces примерно в то же время, когда я писал эту статью). Для начала создадим новый компонент icon, на примере которого мы и будем изучать механизм добавления Ajax внутрь составных компонентов.

Компонент icon с возможностью многократного использования

Представьте, что у вас самая крутая в мире работа – например, вы разрабатываете графический движок следующего поколения для игры World of Warcraft. Но, увы, не сегодня. Сегодня вам надо реализовать выбор размера шрифта (см. рисунок 1):

Рисунок 1. Выбор размера шрифта
Font selector
Font selector

Ваше начальство живо интересуется, сколько времени займет реализации подобной функции. Они хотят, чтобы вы сделали две пиктограммы + и - , так чтобы при нажатии на любую из них размер шрифта символов в окне предварительного просмотра изменялся соответствующим образом. Естественно, они хотят, чтобы вы использовали технологии Ajax, для того чтобы размер шрифта символов в окне предварительного просмотра и значение размера шрифта, отображаемое рядом с пиктограммами + и –, изменялись динамически, никак не затрагивая при этом содержимого остальных частей страницы.

Ваше начальство рассчитывает получить простой компонент для выбора размера шрифта, но вам такое решение не подходит. Вы собираетесь разработать универсальный компонент icon с поддержкой Ajax, который подгружает необходимое графическое изображение и соответствующие события уже в процессе выполнения приложения. Затем вы собираетесь использовать этот компонент icon в окне предварительного просмотра шрифта. Таким образом, вы решите поставленную задачу и получите весьма удобный универсальный компонент, который сможете не раз использовать в будущем.

А теперь я расскажу вам, как создать компонент icon, на реализацию которого требуется менее 25 строк XML-кода.

Приложение для выбора размера шрифта

Приложение для выбора размера шрифта включает в себя четыре файла:

  • Страница, которая показана на рисунке 1 и задается файлом index.xhtml.
  • Компонент icon, заданный в файле /resources/util/icon.xhtml.
  • Обработчик событий (com.clarity.FontSelectionListener.java).
  • Bean-компонент (com.clarity.FontSettings).

Дерево каталогов исходного кода показано на рисунке 2:

Рисунок 2. Структура каталогов исходного кода приложения
Directory structure
Directory structure

В листинге 1 приведен facelet — index.xhtml — для страницы, показанной на рисунке 1:

Листинг 1. Facelet (/web/index.xhtml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html" 
      xmlns:corejsf="http://corejsf.com"
      xmlns:util="http://java.sun.com/jsf/composite/util">

   <h:head>
      <h:outputStylesheet library="css" name="styles.css"/>
      <title>#{msgs.windowTitle}</title>
   </h:head>
   
   <h:body> 
      <h:outputStylesheet library="css" name="styles.css"/>
      
      <h:outputText value="#{msgs.fontSizeHeading}"
        style="padding-left: 30px; font-size: 2em;"/>
      
      <h:panelGrid columns="3" style="padding-left: 80px;">
        <util:icon id="minus" image="#{resource['images:minus.gif']}">
          <f:actionListener for="link" type="com.clarity.FontSelectionListener"/>
        </util:icon>

        <util:icon id="plus" image="#{resource['images:plus.gif']}">
          <f:actionListener for="link" type="com.clarity.FontSelectionListener"/>

        </util:icon>
        
        <h:outputText id="readout" value="#{fontSettings.size}em"/>
      </h:panelGrid>
            
      <h:outputText id="fontPreview" value="Aa" 
                 style="font-size: #{fontSettings.size}em; font-style: italic"/>

   </h:body>
</html>

Facelet, приведенный в листинге 1, определяет пространство имен для компонента icon и использует этот компонент при формировании страницы. Подробное описание правил использования составных компонентов вы найдете в статье JSF 2 fu: Часть 2. Шаблоны и составные компоненты.

Обратите внимание, для обеих пиктограмм компонент link содержит указатель на обработчик событий. Когда пользователь щелкнет на пиктограмме, JSF передаст управление обработчику событий на сервере. Код обработчика событий приведен в листинге 2:

Listing 2. The listener (com/clarity/FontSelectionListener.java)
package com.clarity;

import javax.el.ELResolver;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;

public class FontSelectionListener implements ActionListener {
   @Override
   public void processAction(ActionEvent event)
         throws AbortProcessingException {
    FacesContext c = FacesContext.getCurrentInstance();
    ELResolver elResolver = c.getApplication().getELResolver();
    FontSettings fs = (FontSettings) 
      elResolver.getValue(c.getELContext(), null, "fontSettings");
    
    if (((UIComponent)event.getSource()).getClientId().startsWith("minus"))
      fs.decrement();
    else
      fs.increment();
   }
}

В листинге 2 я проверяю идентификатор клиента компонента, от которого пришел вызов. Если это minus, это значит, что пользователь щелкнул на пиктограмме со знаком минус, и, соответственно, я должен уменьшить шрифт символов в окне просмотра. В противном случае размер шрифта должен быть увеличен.

Обратите внимание на то, что обработчик событий использует ссылку на управляемый bean-компонент fontSettings. Обработчик получает эту ссылку с помощью Expression Language Resolver, который умеет искать управляемые bean-компоненты по заданному имени. Код bean-компонента fontSettings приведен в листинге 3.

Листинг 3. Bean-компонент fontSettings (com/clarity/FontSettings.java)
package com.clarity;

import java.io.Serializable;

import javax.inject.Named; 
import javax.enterprise.context.SessionScoped; 

@Named
@SessionScoped
public class FontSettings implements Serializable {
   private static int INCREMENT = 1;
   private int size = 1;
   
   public int getSize() { return size; }
   public void setSize(int newValue) { size = newValue; }

   public void increment() { size += INCREMENT; }
   public void decrement() { size -= INCREMENT; }
}

В трех предыдущих листингах показан весь исходный код нашего приложения, за исключением составного компонента icon. Его мы разберем в следующем разделе.

Реализация составного компонента icon

Составной компонент пиктограммы должен отвечать трем основным требованиям

  • Графическое изображение пиктограммы должно определяться в процессе работы приложения
  • Действие в ответ на щелчок мышки на пиктограмме должно определяться в процессе работы приложения.
  • Компонент должен включать поддержку Ajax.

Первые два требования реализуются в коде, показанном в листинге 4:

Листинг 4. Составной компонент <util:icon>, версия 1 (/resources/util/icon.xhtml)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:composite="http://java.sun.com/jsf/composite">
    
    <composite:interface>
      <composite:attribute    name="image" required="true"/>
      <composite:actionSource name="link"  targets="#{cc.clientId}:iconForm:link"/>
    </composite:interface>

    <composite:implementation>
      <div id="#{cc.clientId}">
         <h:form id="iconForm">
            <h:commandLink id="link" immediate="true">
              <h:graphicImage value="#{cc.attrs.image}"/>
            </h:commandLink>
         </h:form>
       </div>
    </composite:implementation>    
</html>

Компонент icon, приведенный в листинге 4, определяет атрибут image и actionSource с именем link. Этот же actionSource используется в листинге 1 в качестве значения обработчика событий <f:actionListener> атрибута image. Если подобная реализация кажется вам не совсем понятной, я рекомендую ознакомиться со статьей JSF 2 fu: Часть 2. Шаблоны и составные компоненты, в которой обсуждаются аспекты использования actionSource в составных компонентах и разбирается пример с пиктограммой, аналогичный приведенному в листинге 4.

Реализация компонента icon, приведенная в листинге 4, позволяет разработчику страницы задавать графическое изображение пиктограммы и действие по щелчку на ней, однако подобная реализация не позволяет использовать в компоненте Ajax-технологии. В существующей реализации при щелчке мышкой на пиктограмме JSF инициализирует полное обновление всей страницы и в результате обработки события пиктограммы перерисует всю страницу целиком.

Давайте рассмотрим, каким образом разработчик страницы может добавить поддержку Ajax к компоненту icon

Добавление поддержки Ajax с помощью <composite:clientBehavior>

Для того чтобы разработчики страницы могли использовать Ajax для компонента link внутри компонента <util:icon>, я воспользуюсь тегом <composite:clientBehavior> (см. листинг 5):

Листинг 5. Составной компонент <util:icon> версия 2
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:composite="http://java.sun.com/jsf/composite">
    
    <composite:interface>
      <composite:attribute      name="image" required="true"/>
      <composite:actionSource   name="link"  targets="#{cc.clientId}:iconForm:link"/>
     <composite:clientBehavior name="click" 
                              event="action"
                            targets="#{cc.clientId}:iconForm:link"/>
    </composite:interface>

    <composite:implementation>
      <div id="#{cc.clientId}">
         <h:form id="iconForm">
            <h:commandLink id="link" immediate="true">
              <h:graphicImage value="#{cc.attrs.image}"/>
            </h:commandLink>
         </h:form>
       </div>
    </composite:implementation>    
</html>

Тег <composite:clientBehavior> позволяет определять Ajax-события для компонента, содержащегося внутри составного компонента. В листинге 5 я определяю поведение клиента с логическим именем click, которое ассоциируется с реальным событием, когда щелчок по пиктограмме активизирует соответствующую ссылку. Тег <composite:clientBehavior> обладает следующими атрибутами:

  • name: имя события, используемое разработчиками страницы
  • default: может принимать значение true или false. Если этот атрибут имеет значение true, то событие, указанное в атрибуте name, считается событием по умолчанию.
  • event: фактическое имя события.
  • targets: список ID компонентов, разделенных пробелами, которым JSF передает управление.

Теперь разработчики страницы могут добавить Ajax-вызовы в компонент icon, как показано в листинге 6:

Листинг 6. Facelet, версия 2
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html" 
      xmlns:corejsf="http://corejsf.com"
      xmlns:util="http://java.sun.com/jsf/composite/util">

   <h:head>
      <h:outputStylesheet library="css" name="styles.css"/>
      <title>#{msgs.windowTitle}</title>
   </h:head>
   
   <h:body> 
      <h:outputStylesheet library="css" name="styles.css"/>
      
      <h:outputText value="#{msgs.fontSizeHeading}"
        style="padding-left: 30px; font-size: 2em;"/>
      
      <h:panelGrid columns="3" style="padding-left: 80px;">
        <util:icon id="minus" image="#{resource['images:minus.gif']}">
          <f:ajax event="click" render=":readout :fontPreview"/>
          <f:actionListener for="link" type="com.clarity.FontSelectionListener"/>
        </util:icon>

        <util:icon id="plus" image="#{resource['images:plus.gif']}">
          <f:ajax event="click" render=":readout :fontPreview"/>
          <f:actionListener for="link" type="com.clarity.FontSelectionListener"/>
        </util:icon>
        
        <h:outputText id="readout" value="#{fontSettings.size}em"/>
      </h:panelGrid>
            
      <h:outputText id="fontPreview" value="Aa" 
                 style="font-size: #{fontSettings.size}em; font-style: italic"/>

   </h:body>
</html>

B листинге 6 я добавил для пиктограмм тег <f:ajax>. При щелчке мышкой на какой-либо из пиктограмм JSF осуществит Ajax-обращение к серверу, а по получении ответа JSF запустит компоненты readout и fontPreview.

Кроме того, я могу задать значение true атрибуту default тега <composite:clientBehavior> для компонента icon. Это избавит разработчика страницы от необходимости определять событие для щелчка по пиктограмме, сводя функциональность тега <f:ajax> в листинге 6 к <f:ajax render=":readout :fontPreview">.

Заключение

В этой статье я рассказал вам, каким образом разработчики страниц могут добавлять Ajax-функции к готовым составным компонентам, используя удобный, хотя пока еще не документированный, тег JSF 2. И действительно, как вы могли убедиться на конкретном примере, рассматриваемом в этой статье, вы можете добавить всего лишь одну XML-строку в интерфейс составного элемента, и тем самым предоставить разработчикам страниц возможность превратить составные компоненты, выполняющие полные HTTP-запросы, в компоненты, выполняющие Ajax-вызовы.

В следующей публикации серии JSF 2 fu мы перейдем от изучения составных компонентов к использованию Java EE технологий, таких как CDI (Contexts and Dependency Injection), с JSF.


Ресурсы для скачивания


Похожие темы


Комментарии

Войдите или зарегистрируйтесь для того чтобы оставлять комментарии или подписаться на них.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java
ArticleID=766672
ArticleTitle=JSF 2 fu: Добавление поддержки Ajax в готовые составные компоненты
publish-date=10192011