Статьи

RichFaces и частичная обработка представления JSF

RichFaces — это богатая библиотека компонентов для JSF. RichFaces не заменяет стандартную JSF, поэтому вы используете RichFaces либо с реализацией Mojara JSF (Sun RI), либо с реализацией MyFaces. RichFaces просто предоставляет готовые компоненты Ajax для создания приложений на основе Ajax. Еще один способ взглянуть на это — просто множество дополнительных компонентов JSF, помимо того, что обеспечивает стандартный JSF. Эти компоненты включают в себя все необходимое кодирование JavaScript, поэтому вам почти никогда не придется работать с JavaScript напрямую.

Хотя в RichFaces более 100 тегов, вам нужно знать только три основных понятия. Ознакомившись с этими концепциями, вы можете использовать любые теги и функции RichFaces. Концепции: (1) отправка запроса AJAX, (2) частичная визуализация представления JSF, (3) частичная обработка представления JSF. В этой статье рассматривается концепция 3: частичная обработка представления JSF. Возможность обрабатывать только часть представления JSF важна для создания истинных областей AJAX на странице, проверки и производительности. 

Тег a4j: регион

Тег a4j: region в RichFaces, вероятно, является одним из наиболее неправильно понятых тегов, но он предоставляет одну из самых важных функций в RichFaces. При этом обработка на стороне сервера может быть ограничена только определенными компонентами.

Одной из причин недопонимания может быть имя тега. Многие считают, что тег region ограничивает то, что отображается в дереве компонентов JSF (браузером). Однако он используется для противоположной цели, чтобы пометить области в дереве компонентов JSF для обработки на сервере на этапах «Применить значения запроса», «Проверка процесса» и «Обновить значения модели». Возможно, «processRegion» было бы лучшим названием, но мы просто разберемся с тем, что имеем.

Прежде чем мы продолжим, я хочу поблагодарить Ника Белаевского (руководителя проекта RichFaces и соавтора RichFaces DZone Refcard , Exadel ) и Чарли Коуэнса (технический писатель, Exadel ) за помощь в редактировании этой статьи.

Пример

Давайте посмотрим, как работает тег в примере. Предположим, на странице есть пять компонентов ввода. Также предположим, что вам нужно только один или два из них для обработки на сервере. Хотя мы не можем изменить способ отправки формы (будут отправлены все пять полей ввода), мы можем контролировать, какие компоненты будут обрабатываться на сервере. Компоненты, которые не обрабатываются на сервере, не будут проверены и не будут добавлены в модель (на этапе обновления модели).

Предположим, есть следующая страница:

<h:form>
<h:panelGrid>
<h:inputText id="input1" >
<a4j:support event="onblur"/>
</h:inputText>
<h:inputText id="input2" >
<a4j:support event="onblur"/>
</h:inputText>
<h:inputText id="input3" >
<a4j:support event="onblur"/>
</h:inputText>
</h:panelGrid>
</h:form>

По умолчанию вся страница является регионом. Таким образом, запрос, отправленный из любого поля ввода, обработает всю форму.

Предположим, нам нужно только обработать компонент input1. Для этого мы можем поместить компонент внутри тега a4j: region:

<h:panelGrid>
<a4j:region>
<h:inputText id="input1">
<a4j:support event="onblur" />
</h:inputText>
</a4j:region>
<h:inputText id="input2">
<a4j:support event="onblur" />
</h:inputText>
<h:inputText id="input3">
<a4j:support event="onblur" />
</h:inputText>
</h:panelGrid

В этом случае, если input1 инициирует отправку , на сервере будет обрабатываться только input1 , даже если вся форма будет отправлена.

Также возможно вложить регионы:

<h:panelGrid>
<a4j:region>
<a4j:region>
<h:inputText id="input1">
<a4j:support event="onblur" />
</h:inputText>
</a4j:region>
<h:inputText id="input2">
<a4j:support event="onblur" />
</h:inputText>
</a4j:region>
<h:inputText id="input3">
<a4j:support event="onblur" />
</h:inputText>
</h:panelGrid>

В этом примере, если запрос запускается компонентом input1 (компоненты Input на самом деле не запускают события. Событие инициируется a4j: support), тогда на сервере будет обрабатываться только непосредственная область. Если компонент input2 запускает событие, то оба, input1 и input2 будут обработаны. Если компонент input3 инициирует событие, так как оно относится к области по умолчанию, охватывающей всю страницу, все три ввода будут обработаны.

При использовании a4j: region с компонентом action на сервере будут вызываться только действие и прослушиватели, зарегистрированные на этом компоненте:

<a4j:region>
<a4j:commandButton action="#{bean.save}" value="Click Me"/>
</a4j:region>

Использование атрибута renderRegionOnly с a4j: region

Тег a4j: region имеет важный атрибут renderRegionOnly, для которого можно установить значение true или false (по умолчанию false). Этот атрибут ограничивает повторную визуализацию только текущей областью. Давайте возьмем этот фрагмент кода в качестве примера:

<h:panelGrid columns="2">
<h:outputText value="Name:" />
<h:panelGroup>
<h:inputText id="name" value="#{bean.name}" required="true"
requiredMessage="Name is required"
validatorMessage="Must be 3 characters or longer">
<a4j:support event="onblur" />
<f:validateLength minimum="3" />
</h:inputText>
<rich:message for="name" />
</h:panelGroup>
<h:outputText value="Age:" />
<h:panelGroup>
<h:inputText id="age" value="#{bean.age}" size="4" required="true"
requiredMessage="Age is required"
validatorMessage="Invalid age">
<a4j:support event="onblur" />
<f:validateLongRange minimum="0" />
</h:inputText>
<rich:message for="age" />
</h:panelGroup>
</h:panelGrid>

Когда вы выкладываете или выбираете (событие onblur) из первого поля ввода, не вводя ничего или вводя неверный ввод (как показано), отправляется вся форма, и оба поля проверяются. Оба поля обрабатываются и проверяются, потому что по умолчанию вся страница является регионом. Это не совсем то поведение, которое мы хотим.

regiontag1

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

<h:panelGroup>
<a4j:region>
<h:inputText id="name" value="#{bean.name}" required="true"
requiredMessage="Name is required"
validatorMessage="Must be 3 characters or longer">
<a4j:support event="onblur" />
<f:validateLength minimum="3" />
</h:inputText>
<rich:message for="name" />
</a4j:region>
</h:panelGroup>

<h:outputText value="Age:" />
<h:panelGroup>
<a4j:region>
<h:inputText id="age" value="#{bean.age}" size="4" required="true"
requiredMessage="Age is required"
validatorMessage="Invalid age">
<a4j:support event="onblur" />
<f:validateLongRange minimum="0" />
</h:inputText>
<rich:message for="age" />
</a4j:region>
</h:panelGroup>

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

  1. Поместите курсор мыши в поле имени
  2. Не вводя ничего (или вводя менее 3 символов), покиньте поле и поместите мышь в поле возраста
  3. Ошибка отображается рядом с полем имени, как и ожидалось (обрабатывается только это поле)
    regiontag2
  4. Введите отрицательное число в поле возраста и оставьте поле
  5. Ошибка отображается рядом с полем возраста (как и ожидалось). Однако сообщение об ошибке рядом с полем имени очищается (не ожидается)
    regiontag3

Что произошло? Во-первых, сообщения об ошибках JSF обрабатываются по запросу. Во-вторых, rich: message работает так же, как h: message, но компонент всегда отображается независимо от того, есть ошибка или нет.

Итак, как эти два связаны? Когда поле возраста запускало событие, обрабатывалось только поле возраста. Так как поле имени не было обработано, сообщение об ошибке не было поставлено в очередь, а старое теперь отсутствует, так как это новый запрос. Поскольку мы использовали rich: message, пустое строковое значение (или пустая ошибка) заменило исходное сообщение об ошибке в браузере.

Чтобы помочь нам решить эту проблему, мы собираемся использовать атрибут renderRegionOnly. Для обоих тегов a4j: region мы устанавливаем renderRegionOnly = ”true”:

...
<h:panelGroup>
<a4j:region renderRegionOnly="true">
<h:inputText id="name" value="#{bean.name}" required="true"
requiredMessage="Name is required"
validatorMessage="Must be 3 characters or longer">
<a4j:support event="onblur" />
<f:validateLength minimum="3" />
</h:inputText>
<rich:message for="name" />
</a4j:region>
</h:panelGroup>
...
...
<h:panelGroup>
<a4j:region renderRegionOnly="true">
<h:inputText id="age" value="#{bean.age}"
size="4" required="true"
requiredMessage="Age is required"
validatorMessage="Invalid age">
<a4j:support event="onblur" />
<f:validateLongRange minimum="0" />
</h:inputText>
<rich:message for="age" />
</a4j:region>
</h:panelGroup>

Это означает, что любой повторный рендеринг ограничен регионом, из которого был отправлен запрос. Когда мы покидаем поле возраста, ничего не вводя, ошибка имени не очищается, поскольку мы просто ограничиваем обновление текущим регионом (регионом, в котором находится поле возраста).

Желаемый результат достигнут: отображаются оба сообщения об ошибках:
regiontag4

Следует помнить одну вещь: установка re nderRegionOnly = ”true” не определяет, что нужно перерисовывать, а только ограничивает обновления для текущего региона. Что перерисовать, определяется rich: message

Использование renderRegionOnly позволяет создавать истинную область AJAX на странице, то есть все входные данные для обработки на сервере и все выходные данные для повторного рендеринга ограничены этой конкретной областью.

Использование атрибута ajaxSingle

Тесно связанной особенностью региона является атрибут ajaxSingle. Атрибут может быть применен только к одному компоненту, в то время как a4j: region может содержать любое количество компонентов.

<a4j:region>
<a4j:commandButton action="#{bean.save}" value="Click Me"/>
</a4j:region>

Такой же как:

<a4j:commandButton action="#{bean.save}" value="Click Me" 
ajaxSingle="true"/>

И это:

<a4j:region>
<h:inputText id="age" value="#{bean.city}">
<a4j:support event="onblur" />
</h:inputText>
</a4j:region>

Это так же, как это:

<h:inputText id="age" value="#{bean.city}">
<a4j:support event="onblur" ajaxSingle="true"/>
</h:inputText>

Использование атрибута процесса

Другим важным атрибутом является процесс . Он доступен для всех компонентов RichFaces, которые запускают AJAX-запрос, и используется вместе с ajaxSingle . Когда ajaxSingle установлен в true, мы знаем, что будет обработан только этот компонент. Может возникнуть ситуация, когда вам все еще необходимо дополнительно обработать некоторые другие компоненты на странице. Атрибут процесса указывает на эти компоненты или контейнеры.

<h:inputText>
<a4j:support event="onblur" ajaxSingle="true" process="mobile"/>
</h:inputText>
...
<h:inputText id="mobile"/>

Процесс также может указывать на выражение EL или идентификатор компонента контейнера, в этом случае все компоненты внутри контейнера будут обработаны:

<h:inputText>
<a4j:support event="onblur" ajaxSingle="true"
process="infoPanel"/>
</h:inputText>
<h:panelGrid id="infoPanel">
<h:inputText />
<h:dataTable>
...
</h:dataTable>
</h:panelGrid>

Резюме

Частичная обработка дерева является одним из ключевых понятий в RichFaces, поэтому убедитесь, что вы это понимаете. Другой основной концепцией является частичное рендеринг дерева и, в частности, рендеринг контента, который ранее не рендерился. Вы можете прочитать об этом здесь .

Следует иметь в виду, что регионы работают только в контексте запроса AJAX. Регионы не работают с не AJAX-запросом.

Обучение и поддержка RichFaces

Exadel предлагает индивидуальное обучение RichFaces на месте. Ускорьте всю свою команду всего за 1-2 дня ( информация , план ). Exadel также предлагает поддержку RichFaces .

Он является автором «Практических
RichFaces» (Apress) и соавтором RichFaces DZone RefCard . Он написал множество статей, провел обучение и представил на многих конференциях и вебинарах о технологиях RichFaces и RIA. Макс ведет блог о технологиях RichFaces, JavaFX и RIA на http://mkblog.exadel.com