o:validateBean method="validateCopy" does not validate inputs in composite component - jsf

I'm facing a problem using OmniFaces 2.1 o:validateBean with method="validateCopy" when my inputs are composite components. With method="validateActual", it validates as expected.
JSF implementation: Mojarra 2.2.8-jbossorg-1.
My composite component source:
<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="input" />
<composite:clientBehavior name="keypress" event="keypress" targets="input" />
</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>
I tracked down to the omnifaces source and got to the source point below, in the o:validateBean:
ValueReference valueReference = getValueReference(context.getELContext(), valueExpression);
if (valueReference.getBase().equals(base)) {
operation.run((EditableValueHolder) component, valueReference);
}
In a case where I use a simple input text, the valueReference().getBase() returns my to be validated bean. In a case where I use a composite component, the valueReference().getBase() returns a reference to CompositeComponentAttributesELResolver.ExpressionEvalMap.
Is threre a way to change my composite component so that it works with o:validateBean?

Related

Referencing components inside of composite components (and vice versa)

I'm having one or two problems with Primefaces (v5.2):
Referencing components inside composite components
Let's say I have a composite component that wraps an inputfield:
myinputfield.xhtml
<composite:interface>
<composite:attribute name="value" />
...
</composite:interface>
<composite:implementation>
<h:inputText value="#{cc.attrs.value}" />
</composite:implementation>
(Of course the real application does "a little" more.)
In my page I now use this field like this:
index.xhtml
<my:myinputputfield value=#{controller.inputstring} />
This works. But:
Know I want to reference that inner inputfield from outside, for example for labels or messages. Something like:
<p:inputLabel for="mif" value="Your Input:"/>
<my:myinputputfield id="mif" value=#{controller.inputstring} />
<p:message for="mif" />
Of course that doesn't work, because id isn't defined for myinputfield.
So the first idea that pops to mind is to extent the cc like this:
myinputfield.xhtml (new)
<composite:interface>
<composite:attribute name="id" />
<composite:attribute name="value" />
...
</composite:interface>
<composite:implementation>
<h:inputText id="{cc.attrs.id}" value="#{cc.attrs.value}" />
</composite:implementation>
Which does not work as well. I tried different things and read different answers and articles without finding an answer to this.
The second problem is the complete opposite:
Referencing components outside composite components
This time imagine it the other way around. I have a customized label, message or in my case a tooltip:
mytooltip.xhtml
<composite:interface>
<composite:attribute name="for" />
<composite:attribute name="value" />
...
</composite:interface>
<composite:implementation>
<p:toolTip for="#{cc.attrs.for}" value="#{cc.attrs.value}" />
</composite:implementation>
This time I want to attach mytooltip to an existing component:
index.xhtml
<h:outputtext id="ot" value="Hello World!" />
<my:mytooltip for="ot" value="since 1974" />
Which also does not work. (Of course!?)
This problem I had some time ago and solved it by inclduing the outputText in the composite component.
But I have the feeling it should be possible to manage both user cases. But how?
Referencing components inside composite components
give the internal input a static id
<composite:interface>
<composite:attribute name="value" />
...
</composite:interface>
<composite:implementation>
<h:inputText id="input" value="#{cc.attrs.value}" />
</composite:implementation>
reference the internal component as with any naming container:
<p:inputLabel for="mif:input" value="Your Input:"/>
<my:myinputputfield id="mif" value=#{controller.inputstring} />
<p:message for="mif:input" />
Referencing components outside composite components
the canonical way is to use the full client id:
<h:form id="form">
<h:outputText id="ot" value="Hello World!" />
<my:mytooltip for=":form:ot" value="since 1974" />
</h:form>
but, since you are passing the search expression to a PF component, you can also:
<h:form>
<h:outputText id="ot" value="Hello World!" />
<my:mytooltip for="#form:ot" value="since 1974" />
</h:form>
or generically:
<p:tabView>
<p:tab title="random tab">
<h:outputText id="ot" value="Hello World!" />
<my:mytooltip for="#namingcontainer:ot" value="since 1974" />
</p:tab>
</p:tabView>
or even:
<h:outputText value="Hello World!" />
<my:mytooltip for="#composite:#previous" value="since 1974" />
however, in such cases, a tag-component/facelet-tag-file could be a better approach.

Delegate actionsource to inner composite component

Please help to find out the problem.
I have two composite components:
ajaxCommandButton:
<cc:interface>
<cc:actionSource name="ajaxAction" targets="ajaxCmd" />
<cc:attribute name="action" targets="ajaxCmd" method-signature="void action()" />
<cc:attribute name="value" />
<cc:attribute name="title" />
</cc:interface>
<cc:implementation>
<h:commandButton value="#{cc.attrs.value}" id="ajaxCmd"
title="#{cc.attrs.title}" >
<f:ajax />
</h:commandButton>
</cc:implementation>
and treeItem:
<cc:interface>
<cc:actionSource name="itemAction" targets="ajaxAction" />
</cc:interface>
<cc:implementation>
<ccb:ajaxCommandButton value="test" action="#{action()}" id="ajaxAction">
<f:setPropertyActionListener for="ajaxAction" value="#{value}" target="#{beanProperty}" />
</ccb:ajaxCommandButton>
</cc:implementation>
and usage from ui:composition
<ccb:treeItem>
<f:setPropertyActionListener for="ajaxAction" value="#{value}" target="#{beanProperty}" />
</ccb:treeItem>
What right way to set actionSource from ui:composition to ccb:ajaxCommandButton ?
Thanks in advance!

Label of composite component and o:validateMultipleFields

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.

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.

Required message for pe:inputNumber

I use composite component which has InputNumber from Primefaces extensions in its basis. I've set required attribute to true and message is not shown. Also I don't have * mark which indicates that the field is required.
Here is the code:
<p:outputLabel for="maxvrednost" value="#{resources['skale.maxvrednost']}" />
<asw:inputDecimal id="maxvrednost" bean="#{attrsBean}" column="maxvrednost" required="true" disabled="#{tip == 'brisanje'}" value="#{dto.maxvrednost}"/>
<p:message for="maxvrednost" display="icon" />
Code for composite component is:
<cc:interface>
<cc:attribute name="bean" required="true" type="asw.iis.common.ui.beans.CommonListBackingBean" />
<cc:attribute name="column" required="true" type="java.lang.String" />
<cc:attribute name="value" required="true" type="java.lang.Object" />
<cc:attribute name="disabled" default="false" required="false" type="java.lang.Boolean" />
<cc:attribute name="title" required="false" type="java.lang.String" default=""/>
</cc:interface>
<cc:implementation>
<pe:inputNumber emptyValue="" style="text-align: right;" value="#{cc.attrs.value}" required="#{cc.attrs.required}"
decimalSeparator="#{applicationPropertiesBean.decimalSeparator}" disabled="#{cc.attrs.disabled}"
decimalPlaces="#{cc.attrs.bean.findNumberOfDecimalPlaces(cc.attrs.column)}" title="#{cc.attrs.title}"
thousandSeparator="#{applicationPropertiesBean.groupSeparator}">
</pe:inputNumber>
</cc:implementation>
Not a 100% proper solution but it works for me:
<composite:interface >
<composite:attribute name="value" required="false" type="java.lang.String" default=""></composite:attribute>
<composite:attribute name="update" required="false" type="java.lang.String" default=""></composite:attribute>
<composite:attribute name="process" required="false" type="java.lang.String" default=""></composite:attribute>
<composite:attribute name="requiredMessage" required="false" type="java.lang.String" default=""></composite:attribute>
<composite:attribute name="decimalPlaces" required="false" type="java.lang.Integer" default="0"></composite:attribute>
<composite:editableValueHolder name="value" targets="num"></composite:editableValueHolder>
<composite:
</composite:interface>
<composite:implementation>
<div id="#{cc.clientId}">
<pe:inputNumber id="num" roundMethod="S" decimalPlaces="#{cc.attrs.decimalPlaces}" symbol="#{applicationBean.currentCurrencySymbol}"
minValue="0" required="true" requiredMessage="#{cc.attrs.requiredMessage}"
value="#{cc.attrs.value}"></pe:inputNumber>
</div>
</composite:implementation>
Use as follows:
<p:outputLabel value="Enter Amount" for="amt:num"></p:outputLabel>
<p:message for="amt:num"></p:message>
<comp:InputCurrency id="amt" roundMethod="S" decimalPlaces="0" symbol="#{applicationBean.currentCurrencySymbol}"
minValue="1" requiredMessage="#{loc._('Please enter amount to withdraw!')}"
value="#{myBean.amount}"></comp:InputCurrency>
Note that 'required' attribute is hardcoded to 'true'. If I define composite component attribute 'required' and copy the value to inputNumber 'required' property it behaves as required, but does not render the '*' mark on the label. This happens because inputNumber's 'required' attribute has not been set at render time, comes back as 'false' - not sure why, didnt have time to dig into this.

Resources