В моей предыдущей статье было расмотено два способа создания переиспользуемых компонентов в JSF: с использованием ui:include или с помощью композитных компонентов. Второй способ был рассмотрен не в полной мере, так что продолжим.
Первый не рассмотренный момент — это возможность передавать в качестве значения атрибута MethodExpression. Переданный таким образом метод вы не сможете "вызвать", но сможете передать его в качетве атрибута другим тегам — например, в качестве атрибута action у h:comandLink. Для объявления такого атрибута можно воспользоваться двумя методами:
- Указать сигнатуру ожидаемого метода через атрибут method-signature в формате
ReturnType methodName(ArgumentType1, ArgumentType2, ...)
-
Использовать предопределенные имена “action”, “actionListener”, “validator”, и “valueChangeListener”.
Второй способ можно использовать только при указании атрибута targets
- списка идентификаторов элементов, к которым будет "привязан" метод — он будет привязан к атрибуту, соответствующему имени. Первый способ тоже допускает использование атрибута targets
, но в таком случае необхоимо указание targetAttributeName
. Рассмотрим на примере.
Компонент:
<f:view xmlns="http://www.w3c.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets" xmlns:composite="http://java.sun.com/jsf/composite"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<composite:interface>
<!--Данный метод будет использоваться в качестве action у кнопки btn-->
<composite:attribute name="action" targets="btn"/>
<!--Доступ к данному методу осуществляется напрямую через EL cc.attrs.someAction-->
<composite:attribute name="someAction" required="true" method-signature="java.lang.String action()"/>
<!--Данный метод будет использован как action (targetAttributeName) у кнопок btn-3 и btn-4 (targets)-->
<composite:attribute name="multipleAction" targets="btn-3 btn-4" method-signature="java.lang.String action()"
targetAttributeName="action"/>
</composite:interface>
<composite:implementation>
<h:panelGroup id="panel">
<h:panelGroup id="nestedPanel">
<h:commandButton value="Action button" id="btn"/>
<h:commandButton value="Manually specified action" id="btn-2" action="#{cc.attrs.someAction}"/>
<h:commandButton value="Target 1" id="btn-3"/>
<h:commandButton value="Target 2" id="btn-4"/>
</h:panelGroup>
</h:panelGroup>
</composite:implementation>
</f:view>
Managed Bean:
package com.example.jsf.jsf.beans;
import javax.annotation.ManagedBean;
@ManagedBean
public class TestBean {
public String action() {
System.out.println("Action called");
return "/view.html?faces-redirect=true&arg=" + 1;
}
public String someAction() {
System.out.println("Some action called");
return "/view.html?faces-redirect=true&arg=" + 2;
}
public String multipleAction() {
System.out.println("Multiple action called");
return "/view.html?faces-redirect=true&arg=" + 3;
}
}
Использование компонента:
<h:form>
<custom:test action="#{testBean.action}" someAction="#{testBean.someAction}"
multipleAction="#{testBean.multipleAction}"/>
</h:form>
Во-вторых, композитный компонент может быть источником событий, что позволит добавлять к нему обработчики событий (например, с помощью f:actionListener). Конечно, вы не сможете определять собственные события, но сможете использовать события от других источников событий в реализации вашего компонента (h:commandButton, h:commandLink и другие компоненты, к которым можно добавлять обработчики событий). Для этого используется тег composite:actionSource c двумя основнми атрибутами:
- targets — идентификаторы компонентов, события от которых будут служить источником события для компонента.
- name — имя события в вашем компоненте, оно будет использоваться для добавления обработчика. Если не указан атрибут targets, то будут использоваться события от компонента с таким же идентификатором.
Пример компонента:
<f:view xmlns="http://www.w3c.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:composite="http://java.sun.com/jsf/composite" xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:p="http://java.sun.com/jsf/html">
<composite:interface>
<!--В качестве источника событий будет служить компонет с id firstBtn-->
<composite:actionSource name="firstBtn"/>
<!--В качестве источника событий будут служить компонеты с id btn-2 и link-1-->
<composite:actionSource name="event" targets="firstBtn btn-2 link-1"/>
<!--В качестве источника событий будет служить компонет с id btn-2-->
<composite:actionSource name="anotherEvent" targets="btn-2"/>
</composite:interface>
<composite:implementation>
<p:commandButton id="firstBtn" value="Action source for firstBtn and event"/>
<p:commandLink id="link-1" value="Action source for event"/>
<p:commandButton id="btn-2" value="Action source for event and anotherEvent"/>
</composite:implementation>
</f:view>
Пример применения:
<html>
<f:view xmlns="http://www.w3c.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets" xmlns:custom="http://java.sun.com/jsf/composite/custom">
<h:head>
<title>Title here</title>
</h:head>
<h:body style="height: 100vh">
<h:form>
<custom:actionSource>
<f:actionListener for="firstBtn" binding="#{testBean.listener1()}"/>
<f:actionListener for="event" binding="#{testBean.listener2()}"/>
<f:actionListener for="anotherEvent" binding="#{testBean.listener3()}"/>
</custom:actionSource>
</h:form>
</h:body>
</f:view>
</html>
При нажатии на первую кнопку будут вызваны методы listener1 и listener2, при нажатии на ссылку — метод listener1, и при нажатии на вторую кнопку — listener2 и listener3.
В-третьих, компоненты из реализации, содержащие значение, могут выставить наружу интерфейс для добавления валидаторов, конвертеров, обработчиков изменения значения (f:validator, f:converter, f:valueChangeListener) к различным компонентам (h:inputText и другие реализации EditableValueHolder и ValueHolder). Для этого используются теги valueHolder и editableValueHolder с атрибутами name и targets, аналогичными таковым для actionSource.
Пример компонента:
<f:view xmlns="http://www.w3c.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:composite="http://java.sun.com/jsf/composite" xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:p="http://java.sun.com/jsf/html">
<composite:interface>
<!--Прикреплен к компоненту с id output-->
<composite:valueHolder name="output"/>
<!--Прикрелен к компоненту с id input-1-->
<composite:editableValueHolder name="firstInput" targets="input-1"/>
<!--Прикреплен к компонентам с id input-1 и input-2-->
<composite:editableValueHolder name="allInputs" targets="input-1 input-2"/>
</composite:interface>
<composite:implementation>
<h:outputText id="output" value="#{testBean.currentDate}"/>
<br/>
<h:inputText id="input-1" value="#{testBean.value1}"/>
<br/>
<h:inputText id="input-2" value="#{testBean.value2}"/>
</composite:implementation>
</f:view>
Используемый бин:
@ManagedBean
public class TestBean {
private String value1;
private String value2;
public Date getCurrentDate() {
return new Date();
}
public ValueChangeListener getValueChangeListener() {
return event -> System.out.println("Value changed! " + event.getNewValue());
}
}
Использование компонента:
<html>
<f:view xmlns="http://www.w3c.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:custom="http://java.sun.com/jsf/composite/custom">
<h:head>
<title>Title here</title>
</h:head>
<h:body style="height: 100vh">
<h:form>
<custom:valueHolder>
<!--Добавление конвертера к h:outputText-->
<f:convertDateTime for="output" pattern="dd-MM-yyyy HH:mm:ss"/>
<!--Добавление валидатора к h:inputText с id input-1-->
<f:validateLength for="firstInput" minimum="2"/>
<!--Добавление обработчика события изменения значения в h:inputText с id input-1 и input-2-->
<f:valueChangeListener for="allInputs" binding="#{testBean.valueChangeListener}"/>
</custom:valueHolder>
<h:commandButton value="Submit"/>
<h:messages/>
</h:form>
</h:body>
</f:view>
</html>
Последняя возможность, о которой хотелось бы упомянуть, заключается в возможности отображения вложенных в композитный компонент компонентов. Это возможно с помощью тега composite:insertChildren. Продемонстрирую на примере.
Компонент:
<f:view xmlns="http://www.w3c.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:composite="http://java.sun.com/jsf/composite" xmlns:c="http://java.sun.com/jsp/jstl/core">
<composite:interface>
</composite:interface>
<composite:implementation>
<div class="outer">
Outer component
<composite:insertChildren/>
</div>
</composite:implementation>
</f:view>
Использование:
<html>
<f:view xmlns="http://www.w3c.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:custom="http://java.sun.com/jsf/composite/custom">
<h:head>
<title>Title here</title>
</h:head>
<style>
.outer {
background-color: red;
padding: 40px;
height: 100%;
font-size: 80px;
}
.inner {
background-color: green;
height: 50%;
font-size: 50px;
}
</style>
<h:body style="height: 100vh">
<h:form>
<custom:nested>
<div class="inner">
Inner component
</div>
</custom:nested>
</h:form>
</h:body>
</f:view>
</html>
Полученная страница:
Засим откланиваюсь, прощайте.