<cc:interface componentType="inputAutoComplete">
<cc:attribute name="reRender" type="java.lang.String" shortDescription="Provide The ids of the component to be refreshed"></cc:attribute>
<cc:attribute name="baseURL" type="java.lang.String" shortDescription="Provide service method name of frontController API" />
<cc:attribute name="labelValue" required="true" shortDescription="This is the binding value for label similar to drop down label(ItemLable).If key not present ,Then label is must.It will be considered as key "/>
<cc:attribute name="keyValue" shortDescription="This is the binding value for label similar to drop down key(ItemKey) .Its a hidden field"/>
<cc:attribute name="keyType" type="java.lang.String" shortDescription="The value has to be KEY or KEYVALUE . It defines weather labelValue is key or label"/>
<cc:attribute name="isRequired" type="java.lang.String"/>
<cc:attribute name="datatype" type="java.lang.String" required="true" shortDescription="Data type value only to be dynamic or static"/>
<cc:attribute name="varname" type="java.lang.String" shortDescription="If It is a static json list , then varname is must"/>
<cc:attribute name="manageBeanName" shortDescription="If static list to be prepared from ManagedBean then ManagedBean name is must"/>
<cc:attribute name="manageBeanMethodName" type="java.lang.String" shortDescription="If static list to be prepared from ManagedBean then ManagedBean method name is must and method should return String only as return type"/>
<cc:attribute name="onchange" />
<cc:editableValueHolder name="inTxt" />
<cc:clientBehavior name="clientEvent" targets="inTxt" event="change" />
</cc:interface>
<cc:implementation>
<h:inputText id="intext" value="#{cc.attrs.labelValue}" binding="#{cc.userInputbinding}"
onchange="#{cc.attrs.onchange}">
<cc:insertChildren />
</h:inputText>
<p style="display: none;">THIS VALUE IS INVALID</p>
<h:inputText style="display:none" styleClass="hiddenClass" value="#{cc.attrs.keyValue}" />
<h:panelGroup rendered="#{cc.includeJsonInPage}">
</h:panelGroup>
<f:param name="baseURL" value="#{cc.attrs.baseURL}"/>
<f:param name="datatype" value="#{cc.attrs.datatype}"/>
<f:param name="varname" value="#{cc.attrs.varname}"/>
<f:param name="manageBeanName" value="#{cc.attrs.manageBeanName}"/>
<f:param name="manageBeanMethodName" value="#{cc.attrs.manageBeanMethodName}"/>
<f:param name="keyType" value="#{cc.attrs.keyType}"/>
<f:param name="isRequired" value="#{cc.attrs.isRequired}"/>
<f:param name="keyValue" value="#{cc.attrs.keyValue}"/>
</cc:implementation>
Implementation --
<h:panelGroup id="pnlGrp_editCustomerGroupManageLocal">
<asset:inputAutoComplete datatype="static" labelValue="abcd" id="samleAuto" varname="availableTags" >
<f:ajax event="clientEvent" onevent="alert('sfdksd')"></f:ajax>
</asset:inputAutoComplete>
</h:panelGroup>
The above is the implementation of composite component . I am not able to trigger ajax event from inside the component.
Please help me to know what could be the possible issue with the code .
Related
I have this composite component and it works perfectly.
<cc:interface>
<cc:attribute name="listenerController" ... />
</cc:interface>
<cc:implementation>
<p:outputPanel id="selectOnePanel">
<p:selectOneMenu id="selectOne" ... >
<f:selectItems ... />
<p:ajax event="change" listener="#{listenerController.listenerMethod()}" ... />
</p:selectOneMenu>
</p:outputPanel>
</cc:implementation>
But I wanted to listen to the "change" event from outside this component. So I used the "cc:clientBehavior" in the cc:interface. For the purpose of testing I added a couple of "console.out"s.
First I updated the composite component:
<cc:interface>
<cc:attribute name="listenerController" ... />
<cc::clientBehavior name="change" targets="#{cc.clientId}":selectOne" event="change" />
</cc:interface>
<cc:implementation>
<p:outputPanel id="selectOnePanel">
<p:selectOneMenu id="selectOne" ... >
<f:selectItems ... />
<p:ajax event="change" listener="#{listenerController.listenerMethod()}
onstart="console.log(1);" oncomplete="console.log(2); ... />
</p:selectOneMenu>
</p:outputPanel>
</cc:implementation>
And how I would use it:
<xx:compostiteComponentName>
<p:ajax event="change" onstart="console.log(3)"; oncomplete="console.log(4);" />
</xx:compostiteComponentName>
The problem is that the listener in the composite component stops working. All the "onstart"s and "oncomplete"s seem to work, since the console output is:
1
2
3
4
Why does the listener stop working?
1-Composite components targets just local components so no #{cc.id}:
<cc:interface>
<cc:attribute name="listenerController" ... />
<cc::clientBehavior name="change" targets="selectOne" event="change" />
</cc:interface>
2- Don't forget to inform the listener on the usage of composite:
<xx:compostiteComponentName>
<p:ajax event="change" listener="#{listenerController.listenerMethod()}
onstart="console.log(3);" oncomplete="console.log(4); ... />
</xx:compostiteComponentName>
3 - You may want to remove the ajax event on the selectOneMenu, because when you especify a ajax behavior on the composite, it is attached to the target component and it will not replace the existings ones, wich means that leaving that way the listener will be called two times.
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.
I try to use composite components in my application. When i have added first component it has worked, but when i have added component once again, i have gotten error:
"javax.servlet.ServletException: contains an unknown id 'j_idt202:kladrRegion-dctKladrRegion' - cannot locate it in the context of the component searchButton"
This is code of implementation:
<cc:interface>
<!-- properties -->
<cc:attribute name="isDisabled" type="java.lang.Boolean" />
<cc:attribute name="freeForm" type="java.lang.String" />
<cc:attribute name="houseNum" type="java.lang.String" />
<cc:attribute name="korpusNum" type="java.lang.String" />
<cc:attribute name="flatNum" type="java.lang.String" />
<cc:attribute name="kladrRegion" type="java.lang.String" />
<cc:attribute name="cbKladrRegion" type="javax.faces.component.html.HtmlSelectOneMenu" />
<cc:attribute name="kladrRegions" type="java.util.List" />
<cc:attribute name="kladrCity" type="java.lang.String" />
<cc:attribute name="cbKladrCity" type="javax.faces.component.html.HtmlSelectOneMenu" />
<cc:attribute name="kladrCities" type="java.util.List" />
<cc:attribute name="kladrSearch" type="java.lang.String" />
<cc:attribute name="cbKladrSearch" type="org.icefaces.ace.component.combobox.ComboBox" />
<cc:attribute name="kladrSearches" type="java.util.List" />
<!-- methods -->
<cc:attribute name="listener"
method-signature="void actionListener(javax.faces.event.AjaxBehaviorEvent)"/>
<cc:attribute name="kladrRegionChange"
method-signature="void actionListener(javax.faces.event.AjaxBehaviorEvent)"/>
<cc:attribute name="kladrCityChange"
method-signature="void actionListener(javax.faces.event.AjaxBehaviorEvent)"/>
<cc:attribute name="kladrDlgCloseClick"
method-signature="void actionListener(javax.faces.event.AjaxBehaviorEvent)"/>
<cc:attribute name="kladrDlgSaveClick"
method-signature="void actionListener(javax.faces.event.AjaxBehaviorEvent)"/>
<cc:attribute name="searchKladrAction"
method-signature="java.lang.String action(java.lang.String)"/>
</cc:interface>
<cc:implementation>
<h:panelGrid styleClass="panelGrid" columns="2">
<h:panelGroup styleClass="panelGroup">
<div class="searchWrap">
<h:commandLink id="searchButton"
disabled="#{cc.attrs.isDisabled}"
onclick="openKladrDialog(this, '#{cc.clientId}')">
<h:graphicImage value="/resources/images/search.png"
alt="#{usertag.labelSearchAddr}" />
<f:ajax event="click"
render="kladrRegion-dctKladrRegion"
listener="#{cc.attrs.listener}"/>
</h:commandLink>
</div>
</h:panelGroup>
...etc.
</h:panelGrid>
<!-- dialog -->
<div id="#{cc.clientId}_kladrDialog">
<h:panelGrid styleClass="panelGrid" columns="3">
<h:outputLabel value="Country" class="outputLabel" for="kladrCountry" />
<h:panelGroup styleClass="panelGroup">
<ice:inputText disabled="true" value="Russia"
styleClass="ui-inputfield ui-corner-all inputText kladrSearchFormInput kladrSearchFormInputWidth" />
<h:inputHidden id="kladrCountry" value="179" />
</h:panelGroup>
<h:outputLabel />
<h:outputLabel value="Region" class="outputLabel" />
<h:selectOneMenu id="kladrRegion-dctKladrRegion"
value="#{cc.attrs.kladrRegion}" >
<f:selectItems value="#{cc.attrs.kladrRegions}" var="region"
itemLabel="#{region.value}" itemValue="#{region.id}" />
<f:ajax render="#this kladrCity-dctKladrCity"
listener="#{cc.attrs.kladrRegionChange}" />
</h:selectOneMenu>
... etc.
</div>
</cc:implementation>
This is code of tag:
<kladr:usertag isDisabled="#{PolicyBean.policyDisabled}"
freeForm="#{PolicyBean.policyAuto.holder.baseAddress.freeForm}"
houseNum="#{PolicyBean.policyAuto.holder.baseAddress.houseNum}"
korpusNum="#{PolicyBean.policyAuto.holder.baseAddress.korpusNum}"
flatNum="#{PolicyBean.policyAuto.holder.baseAddress.flatNum}"
kladrRegion="#{PolicyBean.kladrRegion}"
cbKladrRegion="#{PolicyBean.cbKladrRegion}"
kladrRegions="#{PolicyBean.kladrRegions}"
kladrCity="#{PolicyBean.kladrCity}"
cbKladrCity="#{PolicyBean.cbKladrCity}"
kladrCities="#{PolicyBean.kladrCities}"
kladrSearch="#{PolicyBean.kladrSearch}"
cbKladrSearch="#{PolicyBean.cbKladrSearch}"
kladrSearches="#{PolicyBean.kladrSearches}"
listener="#{PolicyBean.holderKladrDlgOpenClick}"
kladrRegionChange="#{PolicyBean.kladrRegionChange}"
kladrCityChange="#{PolicyBean.kladrCityChange}"
kladrDlgCloseClick="#{PolicyBean.kladrDlgCloseClick}"
kladrDlgSaveClick="#{PolicyBean.kladrDlgSaveClick}"
searchKladrAction="#{PolicyBean.kladrSearchClick(PolicyBean.kladrSearch)}" />
any ideas?
The problem is, that your <h:selectOneMenu id="kladrRegion-dctKladrRegion"> is in another parent component than your <h:commandLink id="searchButton" ....
It will work correctly, if you specify the correct absolute component id (or move your dialog inside the same parent).
See Find component by ID in JSF on how to find the correct component id.
The exception tells you that there is not component with id "kladrRegion-dctKladrRegion" inside the component <h:panelGroup> whid got the generated id "j_idt202".
If you're using PrimeFaces, you can use the EL Function component('id') which returns the correct component id. See http://www.primefaces.org/docs/guide/primefaces_user_guide_5_1.pdf, chapter 11.2.
I have the composite component for dataTable where I would like to add the ajax event="rowToggle". I tried the below options:
<cc:interface>
<cc:attribute name="rows" />
<cc:attribute name="value"
type="org.primefaces.model.LazyDataModel" />
<cc:attribute name="var" />
<cc:attribute name="id" />
<cc:attribute name="rowStyle" required="false"/>
<cc:attribute name="ajaxEvent" required="false" />
</cc:interface>
<cc:implementation>
<p:dataTable value="#{cc.attrs.value}"
rendered="#{not empty cc.attrs.value}" id="#{cc.attrs.id}"
paginator="true" rows="25"
currentPageReportTemplate="Showing {startRecord}-{endRecord} of {totalRecords}"
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="25,50,100" paginatorPosition="bottom"
lazy="true" rowStyleClass="#{cc.attrs.rowStyle}">
<p:ajax event="#{cc.attrs.ajaxEvent}" global="false" />
<c:set target="#{component}" property="var" value="#{cc.attrs.var}"/>
<cc:insertChildren />
</p:dataTable>
</cc:implementation>
And from the main page, I am calling:
<t:lazydatatable var="changeSummary"
value="#{changeLogInspectorBean.changeLogInsertModel}" rows="25"
id="change_overview_insert" ajaxEvent="rowToggle">
It is failing with the below error message:
<p:ajax> Event:#{cc.attrs.ajaxEvent} is not supported.
Can you suggest what wrong I am doing here?
as for the ajax event call in a composite component you need to define the proper event in your table.
Try declaring the attribute like:
<cc:attribute name="rowToggleListener" required="false" />
And the actual call inside the datatable like:
<p:ajax event="rowToggle" listener="#{cc.attrs.rowToggleListener}"/>
And for the component:
<t:lazydatatable var="changeSummary"
value="#{changeLogInspectorBean.changeLogInsertModel}"
rows="25" id="change_overview_insert"
ajaxEvent="#{yourBackingBean.onRowToggle}">
The Mthod inside your backing bean could look like this:
public void onRowToggle(RowToggleEvent evt){
MytoggledRowObject rowObject = ((MytoggledRowObject) event.getData());
}
Hope that helped :)
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.