Label of composite component and o:validateMultipleFields - jsf

I have the following composite component, and I want to use o:validateMultipleFields (o:validateAllOrNone more specifically).
<?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:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:composite="http://java.sun.com/jsf/composite"
>
<composite:interface>
<composite:attribute name="target" />
<composite:attribute name="label"/>
<composite:attribute name="value" />
<composite:attribute name="required" />
<composite:attribute name="size" />
<composite:attribute name="disabled" />
<composite:attribute name="styleInput" required="false" />
<composite:editableValueHolder name="input" targets="input" />
<composite:clientBehavior name="change" event="change" targets="#{cc.attrs.target}" />
<composite:clientBehavior name="keypress" event="keypress" targets="#{cc.attrs.target}" />
</composite:interface>
<composite:implementation>
<p:outputLabel id="label" for="input" value="#{cc.attrs.label}" />
<h:panelGrid columns="3">
<p:inputText id="input" value="#{cc.attrs.value}"
style="#{cc.attrs.styleInput}" size="#{cc.attrs.size}"
disabled="#{cc.attrs.disabled}" required="#{cc.attrs.required}">
</p:inputText>
<p:message for="input" display="icon">
<p:effect type="pulsate" event="load" delay="500" />
</p:message>
</h:panelGrid>
</composite:implementation>
</html>
The validation is working as expected, but the labels of the components specified on the components property are no showing. Instead it's showing the components id's.
<cetcomp:editar id="origem" label="Origem" size="10" />
<cetcomp:editar id="cst" label="CST" size="10" />
<o:validateAllOrNone id="origemCst" components="origem:input cst:input" showMessageFor="origem:input" />

The ValidateMultipleFields extracts the labels from the label attribute of the physical input components. Exactly those labels which would be used in standard JSF validation, too. You indeed have none, they are only set on the <p:outputLabel>.
Add them accordingly:
<p:inputText ... label="#{cc.attrs.label}">
An alternative is to use <o:outputLabel> instead of <p:outputLabel> as the OmniFaces one would automatically copy the label to the associated input component.
<o:outputLabel ... for="input" value="#{cc.attrs.label}" />
<p:inputText id="input" ... />
Update: it turns out that it actually still didn't work. The #{cc} wasn't available while ValidateMultipleFields is extracting the labels. This was fixed as per issue 134 and it will be available in OmniFaces 2.1.

Related

Is it possible to pass on the children of a composite component CC as the cell contents of CC's panelGrid parent? [duplicate]

I have lot's of outputLabel and inputText pairs in panelGrids
<h:panelGrid columns="2">
<h:outputLabel value="label1" for="inputId1"/>
<h:inputText id="inputId1/>
<h:outputLabel value="label2" for="inputId2"/>
<h:inputText id="inputId2/>
...
</h:panelGrid>
I want to have some behaviour for all of them: like same validation or same size for every inputText. So I have created a composite component which just includes an outputLabel and and an inputText
<my:editField value="field1"/>
<my:editField value="field2"/>
But now when I put them in a gridPanel, they do not get aligned depending on the length of the label text. I understand why, but I don't know how to work around.
A composite component gets indeed rendered as a single component. You want to use a Facelet tag file instead. It gets rendered exactly as whatever its output renders. Here's a kickoff example assuming that you want a 3-column form with a message field in the third column.
Create tag file in /WEB-INF/tags/input.xhtml (or in /META-INF when you want to provide tags in a JAR file which is to be included in /WEB-INF/lib).
<ui:composition
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<c:set var="id" value="#{not empty id ? id : (not empty property ? property : action)}" />
<c:set var="required" value="#{not empty required and required}" />
<c:choose>
<c:when test="#{type != 'submit'}">
<h:outputLabel for="#{id}" value="#{label} #{required ? '* ' : ''}" />
</c:when>
<c:otherwise>
<h:panelGroup />
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="#{type == 'text'}">
<h:inputText id="#{id}" value="#{bean[property]}" label="#{label}" required="#{required}">
<f:ajax event="blur" render="#{id}-message" />
</h:inputText>
<h:message id="#{id}-message" for="#{id}" />
</c:when>
<c:when test="#{type == 'password'}">
<h:inputSecret id="#{id}" value="#{bean[property]}" label="#{label}" required="#{required}">
<f:ajax event="blur" render="#{id}-message" />
</h:inputSecret>
<h:message id="#{id}-message" for="#{id}" />
</c:when>
<c:when test="#{type == 'select'}">
<h:selectOneMenu id="#{id}" value="#{bean[property]}" label="#{label}" required="#{required}">
<f:selectItems value="#{options.entrySet()}" var="entry" itemValue="#{entry.key}" itemLabel="#{entry.value}" />
<f:ajax event="change" render="#{id}-message" />
</h:selectOneMenu>
<h:message id="#{id}-message" for="#{id}" />
</c:when>
<c:when test="#{type == 'submit'}">
<h:commandButton id="#{id}" value="#{label}" action="#{bean[action]}" />
<h:message id="#{id}-message" for="#{id}" />
</c:when>
<c:otherwise>
<h:panelGroup />
<h:panelGroup />
</c:otherwise>
</c:choose>
</ui:composition>
Define it in /WEB-INF/example.taglib.xml (or in /META-INF when you want to provide tags in a JAR file which is to be included in /WEB-INF/lib):
<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
version="2.0">
<namespace>http://example.com/jsf/facelets</namespace>
<tag>
<tag-name>input</tag-name>
<source>tags/input.xhtml</source>
</tag>
</facelet-taglib>
Declare the taglib usage in /WEB-INF/web.xml (this is not needed when the tags are provided by a JAR file which is included in /WEB-INF/lib! JSF will auto-load all *.taglib.xml files from /META-INF).
<context-param>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/example.taglib.xml</param-value>
</context-param>
(multiple taglib files can be separated by semicolon ;)
Finally just declare it in your main page templates.
<!DOCTYPE html>
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:my="http://example.com/jsf/facelets"
>
<h:head>
<title>Facelet tag file demo</title>
</h:head>
<h:body>
<h:form>
<h:panelGrid columns="3">
<my:input type="text" label="Username" bean="#{bean}" property="username" required="true" />
<my:input type="password" label="Password" bean="#{bean}" property="password" required="true" />
<my:input type="select" label="Country" bean="#{bean}" property="country" options="#{bean.countries}" />
<my:input type="submit" label="Submit" bean="#{bean}" action="submit" />
</h:panelGrid>
</h:form>
</h:body>
</html>
(the #{bean.countries} should return a Map<String, String> with country codes as keys and country names as values)
Screenshot:
Hope this helps.
There should have been a switch in panelGrid to render composite components separately. I have a solution for this. You can have separate composite components instead of clubbing them together. In each composite component you can use ui:fragments to demarcate the components you want to separately fall under different columns. Following is extract from my inputText.xhtml:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:composite="http://java.sun.com/jsf/composite"
xmlns:ui="http://java.sun.com/jsf/facelets">
<composite:interface>
<composite:attribute name="id" />
<composite:attribute name="value" />
<composite:attribute name="label" />
<composite:attribute name="readonly" />
<composite:attribute name="disabled" />
<composite:attribute name="required" />
</composite:interface>
<composite:implementation>
<ui:fragment id="label">
<h:outputText id="#{cc.attrs.id}Label" value="#{cc.attrs.label}"
for="#{cc.attrs.id}" />
<h:outputLabel value="#{bundle['label.STAR']}"
rendered="#{cc.attrs.required}" styleClass="mandatory"
style="float:left"></h:outputLabel>
<h:outputLabel value=" " rendered="#{!cc.attrs.required}"
styleClass="mandatory"></h:outputLabel>
</ui:fragment>
<ui:fragment id="field">
<h:inputText id="#{cc.attrs.id}" value="#{cc.attrs.value}"
styleClass="#{not component.valid ? 'errorFieldHighlight medium' : 'medium'}"
disabled="#{cc.attrs.disabled}" required="#{cc.attrs.required}"
label="#{cc.attrs.label}" readonly="#{cc.attrs.readonly}">
</h:inputText>
</ui:fragment>
</composite:implementation>
</html>
Now this will not going to align in the form which is inside the panelGrid:
<h:panelGrid width="100%">
<my:inputText label="#{bundle['label.fname']}" value="#{bean.fname}" id="fname"></my:inputtext>
<my:inputText label="#{bundle['label.lname']}" value="#{bean.lname}" id="lname"></my:inputtext>
</panelGrid>
So i have extended the GroupRenderer's encodeRecursive method, to add after label and a before field:
// inside my extended renderer
protected void encodeRecursive(FacesContext context, UIComponent component)
throws IOException {
// Render our children recursively
if (component instanceof ComponentRef
&& component.getId().equals("field")) {
context.getResponseWriter().startElement("td", component);
}
super.encodeRecursive(context, component);
if (component instanceof ComponentRef
&& component.getId().equals("label")) {
context.getResponseWriter().endElement("td");
}
}

How to pass action string for commandButton using f:attribute tag in Composite component

I'm developing composite component in JSF. In that I can pass method names as arguments in following way.
<?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:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:p="http://primefaces.org/ui"
xmlns:composite="http://java.sun.com/jsf/composite">
<!-- INTERFACE -->
<composite:interface>
<composite:attribute name="noOfButtons" type="java.lang.Integer" required="true"/>
<composite:attribute name="actionOnly" type="java.lang.Boolean" required="true"/>
<composite:attribute name="myBean" required="true" type="com.jsf.CRUDBean"/>
<composite:attribute name="myIcons" type="java.lang.String" required="true"/>
<composite:attribute name="myAction" type="java.lang.String" required="true"/>
<composite:attribute name="myDesc" type="java.lang.String" required="true"/>
</composite:interface>
<!-- IMPLEMENTATION -->
<composite:implementation>
<c:forEach begin="0" end="#{cc.attrs.noOfmys - 1}" var="counter"
varStatus="status">
<h:commandButton id="myButton_${counter}"
action="#{cc.attrs.myBean[fn:split(cc.attrs.myAction, ',')[status.index]]}"
image="#{request.contextPath}/resources/images/#{fn:split(cc.attrs.myIcons, ',')[status.index]}"
title="#{fn:split(cc.attrs.myDesc, ',')[status.index]}"
style="padding:2px">
<p:spacer width="5px;" />
</c:forEach>
</composite:implementation>
</html>
This is working perfect.But in my case, I need to pass action in following way.
<c:forEach begin="0" end="#{cc.attrs.noOfmys - 1}" var="counter"
varStatus="status">
<h:commandButton id="myButton_${counter}"
image="#{request.contextPath}/resources/images/#{fn:split(cc.attrs.myIcons, ',')[status.index]}"
title="#{fn:split(cc.attrs.myDesc, ',')[status.index]}"
style="padding:2px">
<f:attribute name="action" value="#{cc.attrs.myBean[fn:split(cc.attrs.myAction, ',')[status.index]]}" />
</h:commandButton>
<p:spacer width="5px;" />
</c:forEach>
But this is not working in my application. Whenever I click commandButton from the page nothing happening at user level. Any help would be thankful.

Composite JSF 2.2 component with validation message not displayed

I working on custom JSF 2.2 components with corporate design.
I have a problem with input text.
The problem is that if I use my component and want to specify <h:message for="id_of_component"/> its not work. Because the id of component is different.
<h:message for="input"/>
<custom:input id="input" required="true"/>
If i specify it inside component it works but it's not what I want. It should be flexible in case anyone want to use message on diferent place than I specify in component.
Component definition (with message):
<!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:h="http://xmlns.jcp.org/jsf/html"
xmlns:cc="http://xmlns.jcp.org/jsf/composite"
xmlns:p="http://xmlns.jcp.org/jsf/passthrough">
<!-- INTERFACE -->
<cc:interface name="input" expert="true">
<cc:attribute name="id" required="false"/>
<cc:attribute name="label" />
<cc:attribute name="value" />
<cc:attribute name="title" />
<cc:attribute name="readonly" default="false" type="java.lang.Boolean" />
<cc:attribute name="required" default="false" type="java.lang.Boolean" />
<cc:attribute name="rendered" default="true" type="java.lang.Boolean" />
<cc:attribute name="placeholder" type="java.lang.String"/>
<cc:clientBehavior name="click" targets=":#{cc.id}" event="click" default="true"/>
<cc:clientBehavior name="change" targets=":#{cc.id}" event="change" default="true"/>
<cc:clientBehavior name="blur" targets=":#{cc.id}" event="blur" default="true"/>
<cc:clientBehavior name="keyup" targets=":#{cc.id}" event="keyup" default="true"/>
</cc:interface>
<cc:implementation>
<h:panelGrid rendered="#{cc.attrs.rendered}" columns="2" id="panel" styleClass="customInputWrapper">
<h:outputLabel title="#{cc.attrs.title}"
value="#{cc.attrs.label}#{cc.attrs.required ? ' *' : ''}" for="#{cc.attrs.id}" />
<h:inputText id="#{cc.attrs.id}" styleClass="customInput" value="#{cc.attrs.value}" p:placeholder="#{cc.attrs.placeholder}" readonly="#{cc.attrs.readonly}" title="#{cc.attrs.title}" required="#{cc.attrs.required}"/>
<h:message for="#{cc.attrs.id}" />
<h:outputStylesheet library="custom" name="customComponentsStyle.css"/>
</h:panelGrid>
</cc:implementation>
</html>
Do you have any solution how to solve this problem?

How to return value from backing bean as composite component attribute

I have the following JSF composite component:
<composite:interface componentType="myComp">
<composite:attribute name="input" type="java.lang.Integer" />
<composite:attribute name="output" type="java.lang.Integer" />
<composite:attribute name="action" method-signature="java.lang.String action()"/>
</composite:interface>
<composite:implementation>
<h:inputText id="input" value="#{cc.attrs.input}" />
</composite:implementation>
Assuming the composite is invoked as
<h:form id="form">
<cc:myComposite id="cc" input="#{bean.input}"
output="#{bean.output}" action="#{bean.action}" />
</h:form>
Once bean.action is invoked it sets a value in bean.output. I need to access this value in my javascript, something like this
document.getElementById('form:cc:output').value
From myComp I can set the attribute with
getAttributes().put("output", output);
But the data resides in the backing bean. Any ideas?
You need a component to submit the 'output' value to the backing bean. Use inputHidden to accomplish this:
<composite:interface componentType="myComp">
<composite:attribute name="input" type="java.lang.Integer" />
<composite:attribute name="output" type="java.lang.Integer" />
<composite:attribute name="action" method-signature="java.lang.String action()"/>
</composite:interface>
<composite:implementation>
<h:inputText id="input" value="#{cc.attrs.input}" />
<h:inputHidden id="output" value="#{cc.attrs.output}" />
</composite:implementation>
Then you'll be able to set the 'output' value using its hidden input element via javascript.

Use Omnifaces Validator in a composition component with the "for" tag

I want to use the o:validator tag from the omnifaces library for a composition component:
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core" xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui"
xmlns:pe="http://primefaces.org/ui/extensions">
<cc:interface>
<cc:attribute name="disabled" />
<cc:attribute name="label" />
<cc:attribute name="validatorMessage" />
<cc:attribute name="required" />
<cc:attribute name="value" required="true" />
<cc:editableValueHolder name="date" targets="textfield"/>
</cc:interface>
<cc:implementation>
<p:outputLabel id="label" for="textfield" value="#{cc.attrs.label}" rendered="#{cc.attrs.label!=null}" />
<p:inputText id="textfield" value="#{cc.attrs.value}" styleClass="dateInputField" required="#{cc.attrs.required}" disabled="#{cc.attrs.disabled}">
<f:convertDateTime locale="de_DE" type="date" />
<!-- some other stuff... -->
</p:inputText>
<p:watermark for="textfield" value="TT.MM.JJJJ" />
<cc:insertChildren/>
</cc:implementation>
The validator gets called like this:
<o:validator validatorId="customGreaterThanValidator" compareTo="#{bean.date}" validatorMessage="given date must be later than #{bean.date}" for="date"/>
But unfortunatly it seems that the validator tag from omnifaces ignores the "for" Attribute. With the original validator tag from JSF the "for" tag works, but not if the value of the "compareTo" tag is a ValueExpression.
I tried to implement a custom taghandler, but that didn't work either.
This is indeed not supported in the current 1.3 <o:validator> (nor <o:converter>). It will come in future 1.4 as per issue 126 (where you can download the snapshot).
The key is to change TagHandler to ValidatorHandler and ConverterHandler respectively so that the TagHandlerDelegate can do its job for composites.

Resources