JSF 2 composite component + clientBehavior - ajax

I have a composite component that is a specialization of PrimeFaces autoComplete.
In some pages I need to use the itemSelect event, so I added
<cc:clientBehavior name="itemSelect" event="itemSelect"
targets="#{cc.attrs.id}" />
in the component interface, but the action is not fired.
What am I missing?
Component code:
<?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:cc="http://java.sun.com/jsf/composite"
xmlns:p="http://primefaces.org/ui" >
<!-- INTERFACE -->
<cc:interface>
<cc:attribute name="value" required="true" />
<cc:attribute name="disabled" default="false" />
<cc:attribute name="required" default="false" />
<cc:attribute name="size" default="25" />
<cc:clientBehavior name="itemSelect" event="itemSelect"
targets="#{cc.attrs.id}" />
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
<p:autoComplete disabled="#{cc.attrs.disabled}"
value="#{cc.attrs.value}"
completeMethod="#{tecnicoBean.completarViajante}" var="t"
itemLabel="#{t.nome}" itemValue="#{t}" minQueryLength="2"
forceSelection="true" converter="entityConverter"
size="#{cc.attrs.size}" queryDelay="700" label="Técnico:"
onclick="this.select()" cache="true">
</p:autoComplete>
</cc:implementation>
</html>
Component usage:
<ezcomp:tecnicos value="#{sessionScope.tecnicoRdv}">
<p:ajax event="itemSelect" process="#this" update=":mainPanel"
listener="#{rdvBean.changeTecnico}" />
</ezcomp:tecnicos>

Finally got it working.
I had to add id="#{cc.attrs.id}" to the p:autoComplete component.

cc:clientBehavior targets - list of local id's. Correct way to use unique id for p:autoComplete
<p:autoComplete id="someUniqueId" ... />
and link it to cc:clientBehavior
<cc:clientBehavior name="itemSelect" event="itemSelect"
targets="someUniqueId" />

Related

OmniFaces validateEquals and composite component

I'm using JSF 2.3 with Omnifaces 3.0.
I'm trying to create a custom component that represents an e-mail text field. Nothing special.
I've got the following code that is perfectly working
<h:form id="formAggiungiUtente">
<p:outputLabel for="inputTextEmail" value="#{msg['email']}"/>
<h:inputText id="inputTextEmail" pt:type="email" value="#{userController.userBean.mail}" class="form-control" required="true" requiredMessage="#{msg['email']} #{msg['campoObbligatorio']}" validatorMessage="#{msg['email']} #{msg['nonValido']}">
<f:validator binding="#{emailJsfValidator}"/>
</h:inputText>
<p:outputLabel for="inputTextSecondEmail" value="#{msg['ripetiMail']}"/>
<h:inputText id="inputTextSecondEmail" pt:type="email" value="#{userController.secondMail}" class="form-control" required="true" requiredMessage="#{msg['ripetiMail']} #{msg['campoObbligatorio']}" validatorMessage="#{msg['ripetiMail']} #{msg['nonValido']}">
<f:validator binding="#{emailJsfValidator}"/>
</h:inputText>
<o:validateEqual id="equal" components="inputTextEmail inputTextSecondEmail" message="#{msg['indirizziMailDiversi']}" />
</h:form>
In other parts of the application, I need the component that represents the e-mail text field, so the first step was to create something like the following
<h:form id="formAggiungiUtente">
<ui:include src="templates/mailTextField.xhtml">
<ui:param name="id" value="inputTextEmail" />
<ui:param name="label" value="#{msg['email']}" />
<ui:param name="value" value="#{userController.userBean.mail}" />
<ui:param name="required" value="true" />
<ui:param name="requiredMessage" value="#{msg['email']} #{msg['campoObbligatorio']}" />
<ui:param name="validatorMessage" value="#{msg['email']} #{msg['nonValido']}" />
</ui:include>
<ui:include src="templates/mailTextField.xhtml">
<ui:param name="id" value="inputTextSecondEmail" />
<ui:param name="label" value="#{msg['ripetiMail']}" />
<ui:param name="value" value="#{userController.secondMail}" />
<ui:param name="required" value="true" />
<ui:param name="requiredMessage" value="#{msg['ripetiMail']} #{msg['campoObbligatorio']}" />
<ui:param name="validatorMessage" value="#{msg['ripetiMail']} #{msg['nonValido']}" />
</ui:include>
<o:validateEqual id="equal" components="inputTextEmail inputTextSecondEmail" message="#{msg['indirizziMailDiversi']}" />
</h:form>
and here the mailTextField.xhtml
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:p="http://primefaces.org/ui"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:pt="http://xmlns.jcp.org/jsf/passthrough"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<p:outputLabel for="#{id}" value="#{label}"/>
<h:inputText
id="#{id}"
pt:type="email"
value="#{value}"
class="form-control"
required="#{required}"
requiredMessage="#{requiredMessage}"
validatorMessage="#{validatorMessage}">
<f:validator binding="#{emailJsfValidator}"/>
</h:inputText>
</ui:composition>
Also, this solution is working but, by reading a lot of answers I've understood that if there're too many parameters may is the case to create a custom component. So I've created the following component by using "cvl" as library name
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:cc="http://xmlns.jcp.org/jsf/composite"
xmlns:p="http://primefaces.org/ui"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:pt="http://xmlns.jcp.org/jsf/passthrough">
<cc:interface>
<cc:attribute name="label" type="java.lang.String" default="#{msg['email']}" />
<cc:attribute name="value" type="java.lang.String" />
<cc:attribute name="required" type="java.lang.Boolean" default="true" />
<cc:attribute name="requiredMessage" type="java.lang.String" default="#{msg['email']} #{msg['campoObbligatorio']}" />
<cc:attribute name="validatorMessage" type="java.lang.String" default="#{msg['email']} #{msg['nonValido']}" />
</cc:interface>
<cc:implementation>
<p:outputLabel for="#{cc.clientId}" value="#{cc.attrs.label}"/>
<h:inputText
id="#{cc.clientId}"
pt:type="email"
value="#{cc.attrs.value}"
class="form-control"
required="#{cc.attrs.required}"
requiredMessage="#{cc.attrs.requiredMessage}"
validatorMessage="#{cc.attrs.validatorMessage}">
<f:validator binding="#{emailJsfValidator}"/>
</h:inputText>
</cc:implementation>
</html>
and now my page is like this
<h:form id="formAggiungiUtente">
<cvl:mailTextField
id="inputTextEmail"
value="#{userController.userBean.mail}"
/>
<cvl:mailTextField
id="inputTextSecondEmail"
value="#{userController.userBean.mail}"
requiredMessage="#{msg['ripetiMail']} #{msg['campoObbligatorio']}"
validatorMessage="#{msg['ripetiMail']} #{msg['nonValido']}"
/>
<o:validateEqual id="equal" components="inputTextEmail inputTextSecondEmail" message="#{msg['indirizziMailDiversi']}" />
</h:form>
The problem is that I don't how to fill the parameter "components" of the validateEqual because the error is always something like the following.
ValidateEqual attribute 'components' must refer UIInput client IDs. Client ID is of type 'javax.faces.component.UINamingContainer'
I've tried a lot of combination but without success.
What I'm doing wrong?
Thanks
by reading a lot of answers I've understood that if there're too many parameter may is the case to create a custom component
This one? When to use <ui:include>, tag files, composite components and/or custom components? It says to create a tag file, not a custom component, let alone a composite component.
If you had used a tag file, then this construct would have worked just fine. Composite components, on the other hand, are naming containers. This means that they add an extra client ID layer over their children, like as <h:form> and <h:dataTable> also do. You should also include the composite component's own id in the client ID search expression. It goes like:
<cc:someComposite id="idOfComposite1" ... />
<cc:someComposite id="idOfComposite2" ... />
<o:validateEqual components="idOfComposite1:idOfInput idOfComposite2:idOfInput" />
Whereby the composite implementation has:
<h:inputText id="idOfInput" ... />
You thus only need to fix your composite component to not reference #{cc.clientId} anymore in the id attribute of a child JSF component. This is outright wrong. It's supposed to be only used on a plain HTML <span> or <div> wrapping the composite component's implementation, and then with the sole reason of Rerendering composite component by ajax.

Bean scope loss when using composite JSF components & action methods never being called

I've been asked to refactor the following code, which works:
<a4j:commandButton type="image"
image="/someImage.gif"
action="#{SomeViewController.someDeleteAction}"
onclick="return confirm('#{msg['a.message']}');"
render="someDataTableWithItems">
<f:setPropertyActionListener
target="#{SomeViewModel.selectedItem}" value="#{item}" />
</a4j:commandButton>
..to an alternative which allowed to use a custom popup to confirm the action. This snippet is embedded into a column inside a rich:dataTable component. #{item} is a reference to the object assigned to the row where this button is (defined in the dataTable as var="item").
I decided to do a JSF composite component (first one I do) to have something I could reuse. It's based on this answer by elias
<!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:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
xmlns:cc="http://java.sun.com/jsf/composite">
<cc:interface>
<cc:attribute name="message" default="#{msg['a.default.message']}" />
<cc:attribute name="header"
default="#{msg['a.default.header']}" />
<cc:attribute name="action" required="true"
method-signature="java.lang.String action()" />
<cc:attribute name="actionListener" required="false"
method-signature="java.lang.String action()" />
<cc:attribute name="value" default="Send" />
<cc:attribute name="cancelBtn" default="#{msg['a.default.cancel']}" />
<cc:attribute name="confirmBtn" default="#{msg['a.default.ok']}" />
<cc:attribute name="render" default="#form" />
<cc:attribute name="type" default="submit" />
<cc:attribute name="image" required="false"/>
<cc:attribute name="tooltip" required="false" />
</cc:interface>
<cc:implementation>
<a4j:commandButton type="#{cc.attrs.type}" rendered="#{empty cc.attrs.actionListener and not empty cc.attrs.image}"
image="#{cc.attrs.image}" value="#{cc.attrs.value}"
oncomplete="#{rich:component('somePopup')}.show()">
<rich:tooltip followMouse="false" showDelay="1000" rendered="#{not empty cc.attrs.tooltip}">
#{cc.attrs.tooltip}
</rich:tooltip>
</a4j:commandButton>
<a4j:commandButton type="#{cc.attrs.type}" rendered="#{not empty cc.attrs.actionListener and not empty cc.attrs.image}"
actionListener="#{cc.attrs.actionListener}"
image="#{cc.attrs.image}" value="#{cc.attrs.value}"
oncomplete="#{rich:component('somePopup')}.show()">
<rich:tooltip followMouse="false" showDelay="1000" rendered="#{not empty cc.attrs.tooltip}">
#{cc.attrs.tooltip}
</rich:tooltip>
</a4j:commandButton>
<a4j:commandButton type="#{cc.attrs.type}" rendered="#{empty cc.attrs.actionListener and empty cc.attrs.image}"
value="#{cc.attrs.value}"
oncomplete="#{rich:component('somePopup')}.show()">
<rich:tooltip followMouse="false" showDelay="1000" rendered="#{not empty cc.attrs.tooltip}">
#{cc.attrs.tooltip}
</rich:tooltip>
</a4j:commandButton>
<a4j:commandButton type="#{cc.attrs.type}" rendered="#{not empty cc.attrs.actionListener and empty cc.attrs.image}"
actionListener="#{cc.attrs.actionListener}" value="#{cc.attrs.value}"
oncomplete="#{rich:component('somePopup')}.show()">
<rich:tooltip followMouse="false" showDelay="1000" rendered="#{not empty cc.attrs.tooltip}">
#{cc.attrs.tooltip}
</rich:tooltip>
</a4j:commandButton>
<rich:popupPanel id="somePopup"
header="#{cc.attrs.header}" autosize="true" resizeable="false">
<p>#{cc.attrs.message}</p>
<a4j:commandButton action="#{SomeViewController.someDeleteAction}" <!-- It should be #{cc.attrs.action} but I just put this for debug -->
value="#{cc.attrs.confirmBtn}" render="#{cc.attrs.render}"
oncomplete="#{rich:component('somePopup')}.hide()">
<cc:insertChildren />
</a4j:commandButton>
<h:commandButton value="#{cc.attrs.cancelBtn}"
onclick="#{rich:component('somePopup')}.hide(); return false;" />
</rich:popupPanel>
</cc:implementation>
</html>
..and replace the previous a4j:commandButton with this:
<my:buttonConfirm type="image" id="someID"
image="/someImage.gif"
action="#{SomeViewController.someDeleteAction}"
message="#{msg['a.confirmation.message']}"
render="someDataTableWithItems"
tooltip="#{msg['a.tooltip.message']}">
<f:setPropertyActionListener for="someID"
target="#{SomeViewModel.selectedItem}" value="#{item}" />
</my:buttonConfirm>
The popup appears and you can cancel the action, but when confirming it, SomeViewModel is reinstantiated again, losing the previous bean in scope.
The scope is a custom view scope got from here
I then changed the scope of the model to this one:
#Component("SomeViewModel")
#ViewScoped
Although I tried to use #ManagedBean instead of #Component, the app gave me autowiring errors so I left #Component. The scope was now kept.
I don't know whether the mix of JSF and Spring annotations on this way may have any other consequences.
However the scope of SomeViewModel was now fine, the f:setPropertyActionListener target was never set and the action #{SomeViewController.someDeleteAction} was never called. I have been unable to debug this (not sure where to put breakpoints to see what happens in the middle).
Thanks in advance for your help.
The <f:setPropertyActionListener> does not attach to a component id, but to an ActionSource. See this answer for how to do it.
Edit:
Basically instead of <f:setPropertyActionListener for="buttonId"> you'll have
<cc:interface>
<cc:actionSource name="source" targets="buttonId" />
…
</cc:interface>
<cc:implementation>
<a4jcommandButton id="buttonId" … />
</cc:implementation>
And point to it by <f:setPropertyActionListener for="source">.
After trying some suggestions and researching a little on my own I concluded that there is some kind of problem when using a4j:commandButton inside a rich:popupPanel. Action methods are not being called and attributes defined on f:setPropertyActionListener are not being setted. I have been unable to find out what is exactly getting lost in there.
I've seen examples on the Internet with a4j:commandButton inside a popup so I am not sure if this is caused by any of my dependencies. I use jsf-api 2.1.19, jsf-impl 2.1.19-jbossorg-1 and richfaces 4.3.5.Final.
This is the workaround I finally did. I hope it can be helpful to anyone with the same problem I had:
confirmButton.xhtml
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
xmlns:cc="http://java.sun.com/jsf/composite">
<cc:interface>
<cc:attribute name="message"
default="Some message" />
<cc:attribute name="header"
default="Some header" />
<cc:attribute name="cancelBtn" default="No" />
<cc:attribute name="confirmBtn" default="Yes" />
<cc:attribute name="type" default="submit" />
<cc:attribute name="icon" required="false" />
<cc:attribute name="image" required="false" />
<cc:attribute name="action"
targets="popupConfirmButton" />
<cc:actionSource name="confirmListeners"
targets="popupConfirmButton" />
</cc:interface>
<cc:implementation>
<a4j:commandButton type="#{cc.attrs.type}"
image="#{cc.attrs.image}"
oncomplete="#{rich:component('popupConfirm')}.show()">
</a4j:commandButton>
<a4j:commandButton id="popupConfirmButton"
style="visibility: hidden;" render="#{cc.attrs.render}">
</a4j:commandButton>
<rich:popupPanel id="popupConfirm" header="#{cc.attrs.header}"
autosized="true" width="475" resizeable="false">
<f:facet name="controls">
<h:outputLink value="#"
onclick="#{rich:component('popupConfirm')}.hide(); return false;" />
</f:facet>
<h:panelGrid columns="2">
<h:graphicImage value="#{cc.attrs.icon}" height="64" width="64" />
<p>#{cc.attrs.message}</p>
</h:panelGrid>
<br />
<div align="right">
<a4j:commandButton value="#{cc.attrs.confirmBtn}"
onclick="#{rich:element('popupConfirmButton')}.click();
#{rich:component('popupConfirm')}.hide();" />
 
<h:commandButton value="#{cc.attrs.cancelBtn}"
onclick="#{rich:component('popupConfirm')}.hide(); return false;" />
</div>
</rich:popupPanel>
</cc:implementation>
</html>
Component usage
<my:confirmButton type="image" image="someButtonImage.gif"
icon="someWarningImage.gif"
action="#{SomeViewController.doStuff}"
message="Some message"
render="someComponentID">
<f:setPropertyActionListener for="confirmListeners"
target="#{SomeViewModel.someProperty}" value="foo" />
</my:confirmButton>

Primefaces event not supported by composite component

I have a problem with composite component. I want use pe:javascript (call from normal site) inside composite component. It's possible ?? I try use insertChildren, but I have error: "Event dataSelect is not supported by composite component"
Composite Component.xhtml:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<!-- INTERFACE -->
<cc:interface>
<cc:attribute name="label"/>
<cc:attribute name="value"/>
<cc:attribute name="pattern"/>
<cc:attribute name="size"/>
<cc:attribute name="required"/>
<cc:attribute name="showButtonPanel" type="java.lang.Boolean" default="false"/>
<cc:attribute name="showOn"/>
<cc:attribute name="converterMessage"/>
<cc:attribute name="koloruj" type="java.lang.Boolean" default="false"/>
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
<c:choose>
<c:when test="#{cc.attrs.koloruj}">
<p:calendar id="dateFrom" label="#{cc.attrs.label}" value="#{cc.attrs.value}" pattern="#{cc.attrs.pattern}" size="#{cc.attrs.size}" required="#{cc.attrs.required}"
showButtonPanel="#{cc.attrs.showButtonPanel}"
showOn="#{cc.attrs.showOn}"
converterMessage="#{cc.attrs.converterMessage}"
beforeShowDay="kolorujKalendarz"
>
<cc:insertChildren/>
</p:calendar>
</c:when>
<c:otherwise>
<p:calendar id="dateFrom" label="#{cc.attrs.label}" value="#{cc.attrs.value}" pattern="#{cc.attrs.pattern}" size="#{cc.attrs.size}" required="#{cc.attrs.required}"
showButtonPanel="#{cc.attrs.showButtonPanel}"
showOn="#{cc.attrs.showOn}"
converterMessage="#{cc.attrs.converterMessage}"
>
<cc:insertChildren/>
</p:calendar>
</c:otherwise>
</c:choose>
</cc:implementation>
</html>
how want I use it
<my:mycalendar id="dateFrom" label="aaa" value="#{wniosekUrlopowyForm.dateFrom}" pattern="yyyy-MM-dd" size="10" required="true"
showButtonPanel="true"
showOn="button"
converterMessage=""
>
<f:convertDateTime id="convertDateFrom" pattern="yyyy-MM-dd" for="dateFrom:dateFrom"/>
<pe:javascript event="dateSelect" execute="setUrlopTime('end');"/>
</my:mycalendar >

JSF 2 -- Composite component with optional listener attribute on f:ajax

I have a composite component that looks something like this:
<!DOCTYPE html>
<html
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:dm="http://davemaple.com/dm-taglib"
xmlns:rich="http://richfaces.org/rich"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:a4j="http://richfaces.org/a4j">
<cc:interface>
<cc:attribute name="styleClass" />
<cc:attribute name="textBoxStyleClass" />
<cc:attribute name="inputTextId" />
<cc:attribute name="labelText" />
<cc:attribute name="tabindex" />
<cc:attribute name="required" default="false" />
<cc:attribute name="requiredMessage" />
<cc:attribute name="validatorId" />
<cc:attribute name="converterId" />
<cc:attribute name="title"/>
<cc:attribute name="style"/>
<cc:attribute name="unicodeSupport" default="false"/>
<cc:attribute name="tooltip" default="false"/>
<cc:attribute name="tooltipText" default=""/>
<cc:attribute name="tooltipText" default=""/>
<cc:attribute name="onfail" default=""/>
<cc:attribute name="onpass" default=""/>
</cc:interface>
<cc:implementation>
<ui:param name="converterId" value="#{! empty cc.attrs.converterId ? cc.attrs.converterId : 'universalConverter'}" />
<ui:param name="validatorId" value="#{! empty cc.attrs.validatorId ? cc.attrs.validatorId : 'universalValidator'}" />
<ui:param name="component" value="#{formFieldBean.getComponent(cc.attrs.inputTextId)}" />
<ui:param name="componentValid" value="#{((facesContext.maximumSeverity == null and empty component.valid) or component.valid) ? true : false}" />
<ui:param name="requiredMessage" value="#{! empty cc.attrs.requiredMessage ? cc.attrs.requiredMessage : msg['validation.generic.requiredMessage']}" />
<ui:param name="clientIdEscaped" value="#{fn:replace(cc.clientId, ':', '\\\\\\\\:')}" />
<h:panelGroup layout="block" id="#{cc.attrs.inputTextId}ValidPanel" style="display:none;">
<input type="hidden" id="#{cc.attrs.inputTextId}Valid" value="#{componentValid}" />
</h:panelGroup>
<dm:outputLabel for="#{cc.clientId}:#{cc.attrs.inputTextId}" id="#{cc.attrs.inputTextId}Label">#{cc.attrs.labelText}</dm:outputLabel>
<dm:inputText
styleClass="#{cc.attrs.textBoxStyleClass}"
tabindex="#{cc.attrs.tabindex}"
id="#{cc.attrs.inputTextId}"
required="#{cc.attrs.required}"
requiredMessage="#{requiredMessage}"
title="#{cc.attrs.title}"
unicodeSupport="#{cc.attrs.unicodeSupport}">
<f:validator validatorId="#{validatorId}" />
<f:converter converterId="#{converterId}" />
<cc:insertChildren />
<f:ajax
event="blur"
execute="#this"
render="#{cc.attrs.inputTextId}ValidPanel #{cc.attrs.inputTextId}Msg"
onevent="on#{cc.attrs.inputTextId}Event" />
</dm:inputText>
<rich:message for="#{cc.clientId}:#{cc.attrs.inputTextId}" id="#{cc.attrs.inputTextId}Msg" style="display: none;" />
<script>
function on#{cc.attrs.inputTextId}Event(e) {
if(e.status == 'success') {
$('##{clientIdEscaped}\\:#{cc.attrs.inputTextId}').trigger($('##{cc.attrs.inputTextId}Valid').val()=='true'?'pass':'fail');
}
}
$('##{clientIdEscaped}\\:#{cc.attrs.inputTextId}').bind('fail', function() {
$('##{clientIdEscaped}\\:#{cc.attrs.inputTextId}, ##{clientIdEscaped}\\:#{cc.attrs.inputTextId}Label, ##{cc.attrs.inputTextId}Msg, ##{cc.id}Msg').addClass('error');
$('##{cc.id}Msg').html($('##{clientIdEscaped}\\:#{cc.attrs.inputTextId}Msg').html());
#{cc.attrs.onfail}
}).bind('pass', function() {
$('##{clientIdEscaped}\\:#{cc.attrs.inputTextId}, ##{clientIdEscaped}\\:#{cc.attrs.inputTextId}Label, ##{cc.attrs.inputTextId}Msg, ##{cc.id}Msg').removeClass('error');
$('##{cc.id}Msg').html($('##{clientIdEscaped}\\:#{cc.attrs.inputTextId}Msg').html());
#{cc.attrs.onpass}
});
</script>
<a4j:region rendered="#{facesContext.maximumSeverity != null and !componentValid}">
<script>
$(document).ready(function() {
$('##{clientIdEscaped}\\:#{cc.attrs.inputTextId}').trigger('fail');
});
</script>
</a4j:region>
</cc:implementation>
</html>
I'd like to be able to add an optional "listener" attribute which if defined would add an event listener to my f:ajax but I'm having trouble figuring out how to accomplish this. Any help would be appreciated.
You need to specify the method-signature attribute of the <cc:attribute> tag in order to treat the attribute value as a method expression. You can use the JSTL view build time tag <c:if> to conditionally add the <f:ajax> tag.
<cc:interface>
<cc:attribute name="listener" method-signature="void listener()" />
</cc:interface>
<cc:implementation>
<h:someComponent>
<c:if test="#{cc.getValueExpression('listener') != null}">
<f:ajax listener="#{cc.attrs.listener}" />
</c:if>
</h:someComponent>
</cc:implementation>
(the #{not empty cc.attrs.listener} won't work as EL would implicitly evaluate the attribute as a value expression)
Then you can use it as follows:
<my:someComposite listener="#{bean.listener}" />
Or when your environment doesn't support EL 2.2, then create a backing component:
#FacesComponent("someComponent")
public class SomeComponent extends UINamingContainer {
public boolean isHasListener() {
return getValueExpression("listener") != null;
}
}
which is to be declared and used as
<cc:interface type="someComponent">
<cc:attribute name="listener" method-signature="void listener()" />
</cc:interface>
<cc:implementation>
<h:someComponent>
<c:if test="#{cc.hasListener}">
<f:ajax listener="#{cc.attrs.listener}" />
</c:if>
</h:someComponent>
</cc:implementation>
I've the same problems too, and my solution was create default value for the action method. I have only to create a class: MyComponent.java that contains all default methods signature.
<cc:interface>
<cc:attribute name="listener" method-signature="void listener()"
default="#{myComponent.doNothing()}" />
</cc:interface>
<cc:implementation>
<h:someComponent>
<f:ajax listener="#{cc.attrs.listener}" />
</h:someComponent>
</cc:implementation>
All of the above didn't work for me unfortunately, so I fiddled around and came up with the following solution.
<cc:interface type="someComponent">
<cc:attribute name="listener" method-signature="void listener()" />
</cc:interface>
<cc:implementation>
<h:someComponent>
<p:ajax listener="#{cc.attrs.listener}" onstart="#{cc.attrs.containsKey('listener') ? '' : 'return false'}" />
</h:someComponent>
</cc:implementation>
This will always bind the ajax behavior, but only execute it if there actually is a listener.

rich:tabPanel and problems when field has required="true"

Let's consider following, simplified example:
We have 2 tabs withing <rich:tabPanel switchType="ajax">, each tab has <h:inputText value="" required="true" /> and at the moment we want to switch from one tab to another, and the inputText is empty (we don't want to submit value from it anyway, we want to go to another tab) we get "Validation Error: Value is required."
The example code:
<!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:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
>
<a4j:form id="mainForm"
reRender="mainForm"
ajaxSubmit="true">
<rich:tabPanel switchType="ajax">
<rich:tab label="TabA" >
<a4j:region>
<h:outputText value="Tab A content" />
<h:inputText value="" required="true" />
</a4j:region>
</rich:tab>
<rich:tab label="TabB">
<a4j:region>
<h:outputText value="Tab B content" />
<h:inputText value="" required="true" />
</a4j:region>
</rich:tab>
</rich:tabPanel>
<rich:messages />
</a4j:form>
</html>
You should add the "immediate" attribute to the tabPanel. To quote the docs this means that the tabPanel:
"...component value must be converted and
validated immediately (that is, during
Apply Request Values phase), rather
than waiting until a Process
Validations phase"
For example:
<rich:tabPanel switchType="ajax" immediate="true">

Resources