JSF2.2, primefaces 4, omnifaces 1.8.11.
In a composite component, how to to declare and use a converter as an attribute which is optional (that is to say it could be not used).
For a exemple
interface
<cc:attribute name="converter" required="false" default="" type="javax.faces.convert.Converter" />
...
implementation 1
----------------
<p:inputText value="#{cc.attrs.value}"
converter="#{cc.attrs.converter}" />
result
------
converter is not executed
implementation 2
----------------
<p:inputText value="#{cc.attrs.value}">
<f:converter converterId="#{cc.attrs.converter}" />
</p:inputText>
result
------
com.sun.faces.application.ApplicationImpl createConverter
SEVERE: JSF1006: Cannot instantiate converter of type
...
javax.faces.FacesException: Erreur d’expression : objet nommé «» non détecté
at com.sun.faces.application.ApplicationImpl.createConverter(...)
implementation 3
----------------
<p:inputText value="#{cc.attrs.value}">
<o:converter converterId="#{cc.attrs.converter}" />
</p:inputText>
result
------
same as f:converter
...
caller
<my:componentC value="#{customController.attribute}"
converter="#{customConverter}" />
<my:componentC value="#{customController.attribute}" />
Finally i create a dummy converter as defined by balusc in http://balusc.omnifaces.org/2006/09/debug-jsf-lifecycle.html
Default converter is defined like this in composite component
<cc:attribute name="converter" required="false" default="dumyConverter"/>
<p:inputText value="#{cc.attrs.value}">
<f:converter converterId="#{cc.attrs.converter}" />
</p:inputText>
and used,
<my:componentC value="#{customController.attribute}"
converter="customConverter" />
or
<my:componentC value="#{customController.attribute}" />
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.
<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 .
I have a whole form with a lot of components including a p:tab
When I click on p:commandButton id=c1 to submit the whole form content:
I need to validate the whole form required messages but I do need to ignore the p:tab required messages fields.
If I click on p:commandButton id=c2 inside the p:tab, I need to validate only the required messages fields inside the p:tab.
What is the best solution for this ? Thanks in advance.
You seem to be using the "God Form" anti-pattern. Everything is thrown together in a single <h:form>. This is a poor design/practice. Most sensible way would be to put the fields and buttons in separate forms so that only the related fields and buttons are in its own form so that the form submit doesn't unnecessarily submit/process/convert/validate irrelvant data in other forms.
See also:
How to use <h:form> in JSF page? Single form? Multiple forms? Nested forms?
If that's not possible due to some (strange?) design restrictions, then there are at least 2 other ways:
If you're using ajax, then you can make use of the process attribute. It defaults to #form which would process the entire form. It accepts a space separated string of (relative) client IDs of input field which you'd like to process during submit.
<p:inputText id="field1" ... required="true" />
<p:inputText id="field2" ... required="true" />
...
<p:inputText id="field3" ... required="true" />
<p:inputText id="field4" ... required="true" />
...
<p:commandButton id="c1" ... process="field1 field2" />
...
<p:commandButton id="c2" ... process="field3 field4" />
See also: Understanding PrimeFaces process/update and JSF f:ajax execute/render attributes
If you're not using ajax, or desire a non-ajax fallback, then just check in the required attribute which button has been pressed. This is easy by checking the presence of the button's client ID in the request parameter map.
<p:inputText id="field1" ... required="#{not empty param[c1.clientId]}" />
<p:inputText id="field2" ... required="#{not empty param[c1.clientId]}" />
...
<p:inputText id="field3" ... required="#{not empty param[c2.clientId]}" />
<p:inputText id="field4" ... required="#{not empty param[c2.clientId]}" />
...
<p:commandButton id="c1" binding="#{c1}" ... />
...
<p:commandButton id="c2" binding="#{c2}" ... />
(note: no additional bean properties necessary for c1 or c2! the code is as-is)
See also How to let validation depend on the pressed button?
You can refactor this somewhat with a more self-documenting variable name:
<c:set var="c1ButtonPressed" value="#{not empty param[c1.clientId]}" />
<c:set var="c2ButtonPressed" value="#{not empty param[c2.clientId]}" />
...
<p:inputText id="field1" ... required="#{c1ButtonPressed}" />
<p:inputText id="field2" ... required="#{c1ButtonPressed}" />
...
<p:inputText id="field3" ... required="#{c2ButtonPressed}" />
<p:inputText id="field4" ... required="#{c2ButtonPressed}" />
...
<p:commandButton id="c1" binding="#{c1}" ... />
...
<p:commandButton id="c2" binding="#{c2}" ... />
FYI you can also process the id of a panel containing the controls you want to validate - example :
<p:outputPanel id="thisPanel">
<p:inputText id="field1" ... required="#{not empty param[c1.clientId]}" />
<p:inputText id="field2" ... required="#{not empty param[c1.clientId]}" />
<p:commandButton id="c2" ... process="thisPanel" />
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.