Статьи

Интеграция Spring & JavaServer Faces: улучшенные шаблоны

С выпуском версии 2.0 шаблоны Facelet стали основной частью спецификации JSF. Используя теги <ui: состав> и <ui: decorate>, довольно сложно создавать сложные страницы, сохраняя при этом чистоту разметки. Шаблоны особенно полезны при создании HTML-форм, но, к сожалению, имеют тенденцию вызывать повторения в ваших файлах xhtml, нарушая принцип СУХОГО (не повторяйте себя) хорошего дизайна программного обеспечения. В рамках проекта, обеспечивающего более глубокую интеграцию между JSF и Spring, я разработал несколько новых компонентов, которые призваны упростить создание шаблонов. Прежде чем углубляться в новые компоненты, давайте посмотрим, как можно создать типичную форму с использованием стандартных шаблонов JSF.

Часто исходной отправной точкой с шаблонами форм является добавление некоторого объемного пространства котельной плиты к каждому входу. Часто вам нужны дополнительные теги <div> или <span> для использования css. Вот типичный пример:

01
02
03
04
05
06
07
08
09
10
11
12
<!-- /WEB-INF/pages/entername.xhtml -->
<ui:decoreate template="/WEB-INF/layout/form.xhtml">
  <h:inputText id="firstName" label="First Name" value="#{bean.firstName}"/>
  <ui:param name="label" value="First Name"/>
  <ui:param name="for" value="firstName"/>
</ui:decorate>
<ui:decoreate template="/WEB-INF/layout/form.xhtml">
  <h:inputText id="lastName" label="Last Name" value="#{bean.lastName}"/>
  <ui:param name="label" value="Last Name"/>
  <ui:param name="for" value="lastName"/>
</ui:decorate>
<!-- Many additional form elements -->
1
2
3
4
5
6
7
8
9
<!-- /WEB-INF/layout/form.xhtml -->
<ui:composition>
  <div class="formElement">
    <span class="formLabel">
      <h:outputLabel for="#{for}" label="#{label}">
    </span>
    <ui:insert/>
  </div>
</ui:composition>

Здесь мы видим, что каждый элемент в форме содержится в <div>, а метки формы заключены в дополнительный <span> . В разметке уже есть некоторое повторение с параметром «for», отражающим идентификатор компонента. Я также дал каждому элементу <h: inputText> атрибут label
для лучшей проверки сообщений об ошибках это повторяется в «метке» <ui: param>. Ситуация начинает ухудшаться, если мы хотим пометить обязательные поля звездочкой:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<!-- /WEB-INF/pages/entername.xhtml -->
<ui:decoreate template="/WEB-INF/layout/form.xhtml">
  <h:inputText id="firstName" label="First Name" value="#{bean.firstName}" required="false"/>
  <ui:param name="label" value="First Name"/>
  <ui:param name="for" value="firstName"/>
  <ui:param name="showAsterisk" value="false"/>
</ui:decorate>
<ui:decoreate template="/WEB-INF/layout/form.xhtml">
  <h:inputText id="lastName" label="Last Name" value="#{bean.lastName}" required="true"/>
  <ui:param name="label" value="Last Name"/>
  <ui:param name="for" value="lastName"/>
  <ui:param name="showAsterisk" value="true"/>
</ui:decorate>
<!-- Many additional form elements -->
1
2
3
4
5
6
7
8
9
<!-- /WEB-INF/layout/form.xhtml -->
<ui:composition>
  <div class="formElement">
    <span class="formLabel">
      <h:outputLabel for="#{for}" label="#{label}#{showAsterisk ? ' *' : ''}">
    </span>
    <ui:insert/>
  </div>
</ui:composition>

Довольно неприятно, что нам нужно передавать элементы <ui: param> , которые дублируют атрибуты, уже указанные в <h: inputText> . Легко видеть, как даже при относительно небольших формах мы получим много дублирования в нашей разметке. Нам нужен способ получить информацию о вставленном компоненте внутри шаблона, даже если мы не знаем, какой это будет компонент. Нам нужно <s: componentInfo> .

Компонент <s: componentInfo> предоставляет переменную, содержащую информацию о вставленном компоненте. Эта информация включает метку , идентификатор клиента компонента и, если компонент требуется . Осмотрев вставленный элемент, мы можем удалить много дубликатов:

1
2
3
4
5
6
7
8
<!-- /WEB-INF/pages/entername.xhtml -->
<ui:decoreate template="/WEB-INF/layout/form.xhtml">
  <h:inputText id="firstName" label="First Name" value="#{bean.firstName}" required="false"/>
</ui:decorate>
<ui:decoreate template="/WEB-INF/layout/form.xhtml">
  <h:inputText id="lastName" label="Last Name" value="#{bean.lastName}" required="true"/>
</ui:decorate>
<!-- Many additional form elements -->
01
02
03
04
05
06
07
08
09
10
11
<!-- /WEB-INF/layout/form.xhtml -->
<ui:composition>
  <s:componentInfo var="info">
    <div class="formElement">
      <span class="#{info.valid ? 'formLabel' : 'formErrorLabel'}">
        <h:outputLabel for="#{info.for}" label="#{info.label}#{info.required ? ' *' : ''}">
      </span>
      <ui:insert/>
    </div>
  </s:componentInfo>
</ui:composition>

Что-то еще, что мы можем теперь сделать, это сказать, если вставленный компонент не прошел проверку. Обратите внимание, что в приведенном выше примере будет выбран CSS-класс formErrorLabel для недопустимых компонентов.

Одна интересная особенность нового компонента <s: componentInfo> заключается в том, что все теги <ui: decorate> становятся идентичными. Мы удалили все повторения внутри тега, но сам тег все еще повторяется много раз. Здесь у нас есть еще один трюк, который может помочь, введя новый тег <s: decorateAll> . Использование <s: decorateAll> позволяет использовать шаблон один раз для каждого дочернего компонента. Вот обновленная разметка формы:

1
2
3
4
5
6
<!-- /WEB-INF/pages/entername.xhtml -->
<s:decoreateAll template="/WEB-INF/layout/form.xhtml">
  <h:inputText id="firstName" label="First Name" value="#{bean.firstName}" required="false"/>
  <h:inputText id="lastName" label="Last Name" value="#{bean.lastName}" required="true"/>
  <!-- Many additional form elements -->
</s:decorateAll>
01
02
03
04
05
06
07
08
09
10
11
<!-- /WEB-INF/layout/form.xhtml -->
<ui:composition>
  <s:componentInfo var="info">
    <div class="formElement">
      <span class="#{info.valid ? 'formLabel' : 'formErrorLabel'}">
        <h:outputLabel for="#{info.for}" label="#{info.label}#{info.required ? ' *' : ''}">
      </span>
      <ui:insert/>
    </div>
  </s:componentInfo>
</ui:composition>

Если вы хотите взглянуть на исходный код этих компонентов, посмотрите пакет org.springframework.springfaces.template.ui в проекте Springfaces GitHub .

Ссылка: Интеграция Spring & JavaServer Faces: Улучшенные шаблоны от нашего партнера JCG Филиппа Уэбба в блоге Фила Уэбба .