Продолжим наши приключения в библиотеке Primefaces. Кто-то, возможно, уже обратил внимание, что несмотря на то, что примерах в предыдущих частях статьи использовались компоненты, которые только выводят данные на страницы, но не посылают изменение данных в код управляемых бинов, все равно везде сразу после тега h:body в коде xhtml страниц я обязательно использую тег h:form и уже внутри него размещаю все остальные компоненты. Связано это как раз с тем, что Primefaces является надстройкой над JSF из Java EE/Jakarta EE и поэтому наследует их особенности. Если заглянуть вглубь компонентов Primefaces, то большинство из них имплементирует интерфейсы EditableValueHolder и ActionSource и поэтому такие компоненты обязательно должны быть обернуты в компонент UIForm. Фактически это требование повторяет требование ванильного html о том, чтобы все теги-инпуты были заключены внутрь тега <form> для того, чтобы значения из инпутов можно было передать в обработчик на сервере.

В Primefaces ситуация примерно такая же, но еще более гибкая. Любой компонент, предназначенный для ввода данных, имеет атрибут value, который привязывает его значение к некоторому полю управляемого бина. Однако, ничто не запрещает нам разместить на странице компоненты, привязанные к нескольким разным бинам, и таким образом, значения, вводимые в инпуты будут попадать в экземляры различных бинов. Каким образом обработать такую ситуацию? В ванильном html у тега формы есть атрибут action, который указывает на привязанный к форме обработчик. В JSF и Primefaces у тега h:form также есть несколько атрибутов, которые можно использовать с аналогичной целью. Но все они являются необязательными, так как вполне можно данные из формы получить и обработать на стороне управляемых бинов с помощью какой-либо кнопки, например:

<p:commandButton value="Сохранить" action="#{employeeCardView.onsubmit()}"/>

При этом ничто не мешает вам указать в атрибуте action через пробел два и более метода каких угодно управляемых бинов, в том числе разных. Это позволит вам собрать данные со страницы, привязанные к разным бинам (из атрибутов value соответствующих компонентов) и обработать их в разных управляемых бинах. Если же вам необходимо выдержать строгий порядок обработки данных, можно вызвать некоторый метод одного управляемого бина, передав в него нужные данные отдельно в качестве параметров, а уже внутри первого бина вызвать два метода из разных бинов строго последовательно, такая необходимость тоже иногда возникает. Таким образом, применение связывания данных в формах в Primefaces представляется мне намного более гибким и дает больше нестандартных интересных возможностей.

Большинство простых инпутов Primefaces не будут представлять для вас абсолютно никаких сложностей, например, приведем для примера самые распространенные p:inputText и p:inputTextArea:

<p:inputText value="#{inputTextView.text}" placeholder="введите текст"/>
<p:inputTextarea id="comment" rows="6" cols="33"
                 value="#{companyCardView.comment}"/>

Более интересны, например, компоненты для управляемых кнопок и линков, они имеют встроенные ajax возможности, что позволяет их использовать, например, для тонко настраиваемого сабмита сложных форм (как я это показал чуть выше), для разделения реакции на стороне управляемого бина при размещении множества однотипных компонентов на одной странице (когда бин должен точно знать, на какой из однотипных компонентов было выполнено ajax действие и отреагировать выполнением какого-то метода с учетом уникальных переданных параметров). Например, следующая кнопка может быть вставлена внутрь любого компонента типа Data List или Data Table и выполнить метод onsubmit управляемого бина с уникальным параметром id элемента списка.

<p:commandButton value="Сохранить" action="#{employeeExtraSkillsSelectionEditableView.onsubmit(skill.id)}"/>

Однако, вас ждут и подводные камни тоже - при составлении композитных компонентов. Например, если вы попытаетесь вставить простые инпуты типа p:inputText, в список, выводимый компонентом p:dataList, ожидая, что вы получите список инпутов, заполненный старыми значениями списка, полученными из бина, которые вы сможете перезаполнить на странице и передать в бин для сохранения результата, вас поджидает странная и неприятная неожиданность. binding такого компонента будет работать таким образом, что у вас ничего не получится, вы все время будете получать старые значения. То есть вот этот код окажется неработоспособным:


<p:datalist 
        value="#{companyCardView.socnetworksList}" var="site">
        <p:inputText value="#{site}">
        </p:inputText>
</p:dataList>

Для решения такой или аналогичной ситуации необходимо вернуться к компонентам стандартной библиотеки jstl, которая входит как в Java EE/Jakarta EE, так и в состав стандартных возможностей ЯП Java. В xhtml файле необходимо подключить ее в заголовке, чтобы пользоваться ею:

<!DOCTYPE html>
<html ................
      xmlns:c="http://java.sun.com/jsp/jstl/core">

А искомый список инпутов нужно заменить на вот такой вариант:

<c:forEach 
           items="#{companyCardView.socnetworksList}" var="site">
        <p:inputText id="socnetwork" value="#{site}">
        </p:inputText>
</c:forEach>

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

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

Также приглашаю всех на бесплатный урок по теме "Знакомство с Java Mission Control", который пройдет уже 18 февраля.

Комментарии (0)