Passing EL expression to composite component - jsf-2

we are still in a JSF 1.2 to 2.0 migration scenario and we are now facing a problem related to c:set or ui:param variables used inside an EL expression.
Here are the facts. There is a button as composite component:
<cc:interface name="button" shortDescription="A button.">
...
<cc:attribute
name="disabled"
required="false"
default="false"
shortDescription="The disabled flag." />
...
</cc:interface>
<cc:implementation>
<ice:commandButton
...
disabled="#{cc.attrs.disabled}"
... />
</cc:implementation>
Now we are trying to use this button component inside a toolbar. The disabled state of the button is determined inside the toolbar using a c:set or a ui:param (we already tried both ways).
<c:set var="isButtonEnabled" value="#{backingBean.buttonEnabled}" />
or
<ui:param name="isButtonEnabled" value="#{backingBean.buttonEnabled}" />
#{isButtonEnabled}
<ctrl:button
...
disabled="#{!isButtonEnabled}"
... />
So here is our problem. If we simple print out the value of "isButtonEnabled" in the toolbar, it is always correct. So the backing bean is ok. But when we try to pass this value to the composite component, it is not working. "Disabled" is always evaluated to false.
Sure we could pass the method expression directly (#{!backingBean.isButtonEnabled}) and this will work fine. But in our scenario the determination of the enabled-flag is much more complicated and I just tried to keep the example as simple as possible. Aditionally this flag is used for multiple buttons inside the toolbar, so we wanted to keep the code maintainable by using a c:set or ui:param. Is this the wrong way to handle this? What do you recommend?
Thanks in advance.
SlimShady

Your problem is the way value binding is done in JSF. The prefered way is to retrieve the EL Expression an attribute was populated with by invoking getValueExpression("attributeName"). Then this EL Expression can be used to get or set the value in the backing bean. As your not passing #{!isButtonEnabled} but #{cc.attrs.disabled} to ice:commandButton the binding fails.
I solved this for the p:selectOneMenu component of Primefaces by writing a wrapping UIComponent which defines a property wrappedValue and passed that property to the p:selectOneMenu. In the getter and setter of that property I then used getValueExpression to retieve the real EL Expression for the attribute.
<composite:interface componentType="de.gw2tome.component.valuewrapper">
<composite:attribute name="value" type="de.gw2tome.models.Rarity"
required="true" />
</composite:interface>
<composite:implementation>
<p:selectOneMenu value="#{cc.wrappedValue}"/>
...
</composite:implementation>
#FacesComponent("de.gw2tome.component.valuewrapper")
public class ValueWrapper extends UINamingContainer {
public void setWrappedValue(Object wrappedValue) {
ValueExpression expr = getValueExpression("value");
ELContext ctx = getFacesContext().getELContext();
expr.setValue(ctx, wrappedValue);
}
public Object getWrappedValue() {
ValueExpression expr = getValueExpression("value");
ELContext ctx = getFacesContext().getELContext();
return expr.getValue(ctx);
}
}
The component can now be used the following way:
<g:rarityChooser value="#{itemSearchBean.minRarity}" />

Related

JSF2.2 Multiple field validation message not appearing.. the message is associated to a component [duplicate]

JSF 2.0 only allows you to validate the input on one field, like check to see if it's a certain length. It doesn't allow you to have a form that says, "enter city and state, or enter just a zip code."
How have you gotten around this? I'm only interested in answers that involve the validation phase of JSF. I'm not interested in putting validation logic in Managed Beans.
The easiest custom approach I've seen and used as far is to create a <h:inputHidden> field with a <f:validator> wherein you reference all involved components as <f:attribute>. If you declare it before the to-be-validated components, then you can obtain the submitted values inside the validator by UIInput#getSubmittedValue().
E.g.
<h:form>
<h:inputHidden id="foo" value="true">
<f:validator validatorId="fooValidator" />
<f:attribute name="input1" value="#{input1}" />
<f:attribute name="input2" value="#{input2}" />
<f:attribute name="input3" value="#{input3}" />
</h:inputHidden>
<h:inputText binding="#{input1}" value="#{bean.input1}" />
<h:inputText binding="#{input2}" value="#{bean.input2}" />
<h:inputText binding="#{input3}" value="#{bean.input3}" />
<h:commandButton value="submit" action="#{bean.submit}" />
<h:message for="foo" />
</h:form>
(please note the value="true" on the hidden input; the actual value actually doesn't matter, but keep in mind that the validator won't necessarily be fired when it's null or empty, depending on the JSF version and configuration)
with
#FacesValidator(value="fooValidator")
public class FooValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
UIInput input1 = (UIInput) component.getAttributes().get("input1");
UIInput input2 = (UIInput) component.getAttributes().get("input2");
UIInput input3 = (UIInput) component.getAttributes().get("input3");
// ...
Object value1 = input1.getSubmittedValue();
Object value2 = input2.getSubmittedValue();
Object value3 = input3.getSubmittedValue();
// ...
}
}
If you declare the <h:inputHidden> after the to-be-validated components, then the values of the involved components are already converted and validated and you should obtain them by UIInput#getValue() or maybe UIInput#getLocalValue() (in case the UIInput isn't isValid()) instead.
See also:
Validator for multiple fields (JSF 1.2 targeted)
Alternatively, you can use 3rd party tags/components for that. RichFaces for example has a <rich:graphValidator> tag for this, Seam3 has a <s:validateForm> for this, and OmniFaces has several standard <o:validateXxx> components for this which are all showcased here. OmniFaces uses a component based approach whereby the job is done in UIComponent#processValidators(). It also allows customizing it in such way so that the above can be achieved as below:
<h:form>
<o:validateMultiple id="foo" components="input1 input2 input3" validator="#{fooValidator}" />
<h:inputText id="input1" value="#{bean.input1}" />
<h:inputText id="input2" value="#{bean.input2}" />
<h:inputText id="input3" value="#{bean.input3}" />
<h:commandButton value="submit" action="#{bean.submit}" />
<h:message for="foo" />
</h:form>
with
#ManagedBean
#RequestScoped
public class FooValidator implements MultiFieldValidator {
#Override
public boolean validateValues(FacesContext context, List<UIInput> components, List<Object> values) {
// ...
}
}
The only difference is that it returns a boolean and that the message should be specified as message attribute in <o:validateMultiple>.
Apache ExtVal was not mentioned here.
There are some cross validations in it (among other validations which might be useful):
https://cwiki.apache.org/confluence/display/EXTVAL/Property+Validation+Usage#PropertyValidationUsage-CrossValidation

Omnifaces Components#includeCompositeComponent set attribute value

When dynamically creating a composite component with Omniface's Components #includeCompositeComponent, is it possible to pass attributes values (such as attribute myValue) so that it is available for a pre-render event on the composite?
<composite:interface>
<composite:attribute name="myValue" required="true" />
</composite:interface>
<composite:implementation>
<!-- this works! -->
<h:outputText value="#{cc.attrs.myValue}" />
<!-- this doesn't execute -->
<f:event id="eventId"
type="javax.faces.event.PreRenderComponentEvent"
listener="#{bean.someAction(cc.attrs.myValue)}" />
<!-- stuff -->
</composite:implementation>
However, I'm able to access myValue on the xhtml (via #{cc.attrs.myValue}), but its not available for pre-render (I used UIComponent#getAttributes to specify myValue's value).
Edit: if the attribute is also available in #PostConstruct (via managed-property) it would also do.
Check the javadoc:
public static UIComponent includeCompositeComponent(UIComponent parent, String libraryName, String resourceName, String id)
...
Returns:
The created composite component, which can if necessary be further used to set custom attributes or value expressions on it.
So, just get hold of the returned UIComponent instance and make use of UIComponent#getAttributes() to put static values or UIComponent#setValueExpression() to set EL expressions.
UIComponent composite = Components.includeCompositeComponent(parent, library, name, id);
composite.getAttributes().put("myValue", myStaticValue);
// Or:
composite.setValueExpression("myValue", Components.createValueExpression("#{bean.myValue}", String.class));
// ...

How to get command link value(display name) from backing bean?

I have a p:commandLink in my xhtml with the value toggling between "Show"/"Hide".
Is there any way by which I can get the value of this commandlink from the backing bean?
I mean, I want to know what value the command link is showing currently i.e. Show/Hide?
To the point, the invoking component is just available in ActionEvent argument:
<h:commandLink id="show" value="Show it" actionListener="#{bean.toggle}" />
<h:commandLink id="hide" value="Hide it" actionListener="#{bean.toggle}" />
with
public void toggle(ActionEvent event) {
UIComponent component = event.getComponent();
String value = (String) component.getAttributes().get("value");
// ...
}
However, this is a poor design. Localizable text should absolutely not be used as part of business logic.
Rather, either hook on component ID:
String id = (String) component.getId();
or pass a method argument (EL 2.2 or JBoss EL required):
<h:commandLink id="show" value="Show it" actionListener="#{bean.toggle(true)}" />
<h:commandLink id="hide" value="Hide it" actionListener="#{bean.toggle(false)}" />
with
public void toggle(boolean show) {
this.show = show;
}
or even just call the setter method directly without the need for an additional action listener method:
<h:commandLink id="show" value="Show it" actionListener="#{bean.setShow(true)}" />
<h:commandLink id="hide" value="Hide it" actionListener="#{bean.setShow(false)}" />
As #BalusC suggested, your approach is not a good solution. But if you really want to do that, you can bind the component (p:commandLink) to your backingbean, as seen in What is the advantages of using binding attribute in JSF?.
After the component was bound, you can access the value attribute from the p:commandLink.

#ManagedProperty initialization

I want to store the selected value of the first attribute managedabean "refCont" in a second attribute of the managed bean and then I'll post. but the value will show that after the second selection knowing that the value displayed is the one selected for the first time. I do not know what's sake
.xhtml
<h:outputText value="* Contact" />
<h:selectOneMenu id="contact" value="#{reclamationMB.refCont}" onchange="lazyload()">
<c:set value="#{reclamationMB.refCont}" target="#{contactMB}" property="refContt" />
<f:ajax listener="#{contactMB.affichReclContact}" />
<f:selectItem itemLabel="Select One" noSelectionOption="true" itemValue="---" />
<f:selectItems value="#{contactMB.contactsClient}" noSelectionValue="" var="contact" itemValue="#{contact.refCont}" itemLabel="#{contact.nomCont}" />
<f:selectItems itemLabel="Autre" itemValue=""></f:selectItems>
</h:selectOneMenu>
first ManagedBean:
#RequestScoped
public class ReclamationMB extends AbstractMB implements Serializable {
private int refCont;//ref contact recuperé pour la creation de la reclamation
.....
//getter and setter
Second Mnaged bean
#RequestScoped
public class ContactMB extends AbstractMB implements Serializable {
#ManagedProperty("#{reclamationMB.refCont}")
private int refContt;
.....
//getter an setter
public void affichReclContact(AjaxBehaviorEvent event){
contact=service.AffichContact(refContt);
setContact(contact);
}
The problem is that JSTL's <c:set> is a view build time tag handler, and not a view render time UI component, so your problem is a classical one: when you expect it to set the value, it is no longer there in the component tree. Moreover, it has run when there was no <h:selectOneMenu> tag at all.
Of course, if <h:selectOneMenu> implemented ActionSource, just like <h:commandButton>, you could have nested JSF <f:setPropertyActionListener> for the reason of setting the bean property with the needed values, as in <f:setPropertyActionListener target="#{contactMB.refContt}" value="#{reclamationMB.refCont}">. But you can't do that.
Basically you can do two things:
Use PrimeFaces <p:remoteCommand> tag, which can hold the abovementioned action listener to set the needed value, and call it in onchange method. There are a bunch of such examples here on Stack Overflow and in excellent PrimeFaces' documentation.
Make use of bean injection, instead of just bean property injection. In the former case, the expression reclamationMB.getRefCont() will always yield the right result in the holder bean, provided that reclamationMB is the variable to hold the injected bean by #ManagedProperty.

JSF Composite component : Error occured when an attribute i.e. valueChangeListener is not available

I have a composite (X) like this:
<composite:interface>
<composite:attribute name="textValue" />
<composite:attribute name="textValueChangeListner"
method-signature="void valueChanged(javax.faces.event.ValueChangeEvent)" />
<composite:implementation>
<ice:inputText
value="#{cc.attrs.textValue}"
valueChangeListener="#{cc.attrs.textValueChangeListner}"/>
In the JSF page I have something like:
<X:iText
textValue="#{cardBean.getCardValue}"
textValueChangeListner="#{cardHandler.cardValueChanged}" />
The above code works fine. But it does not work when NO "textValueChangeListner" is passed to the composite from JFace page; i.e:
<X:iText
textValue="#{cardBean.getCardValue}" />
Error I got:
[javax.enterprise.resource.webcontainer.jsf.lifecycle] Unable to resolve composite component from using page using EL expression '#{cc.attrs.textValueChangeListner}': javax.faces.FacesException: Unable to resolve composite component from using page using EL expression '#{cc.attrs.textValueChangeListner}'
In my scenario it is necessary that page developer may or may not supply the "textValueChangeListner" to the composite component.
How can I achieve that ?
Rename the attribute to valueChangeListener and use targets attribute,
<composite:attribute name="valueChangeListner"
method-signature="void valueChanged(javax.faces.event.ValueChangeEvent)"
targets="text" />
where text is the ID of the target input component,
<ice:inputText id="text" ... />
and remove the valueChangeListener from it.
You have two options here: First one is the easy way, just render another input text without having a valueChangeListener if the user didn't supply Value Change listener.
<ice:inputText
value="#{cc.attrs.textValue}"
valueChangeListener="#{cc.attrs.textValueChangeListener}"
rendered="#{!empty cc.attrs.textValueChangeListener}"/>
If you need to have a valueChangeListener in any case then apply an default value for the attribute:
<composite:attribute name="textValueChangeListener"
method-signature="void valueChanged(javax.faces.event.ValueChangeEvent)"
required="false"
default="#{cc.valueChangedListener}" />
For doing this you need to bind your composite component to a backing bean. And you need to define components' backing bean type through its interface declaration like this:
<composite:interface componentType="yourComponentBean">
<composite:attribute name="textValueChangeListener"
method-signature="void valueChanged(javax.faces.event.ValueChangeEvent)"
required="false"
default="#{cc.valueChangedListener}" />
</composite:interface>
And here is yourComponentBean which implements the default ValueChangedListener.
#FacesComponent("yourComponentBean")
public class YourComponentCC extends UINamingContainer
{
public void valueChangedListener(ValueChangeEvent ev)
{
....
}
}
Btw cc is a shortcut in EL to access backing bean that is defined as the componentType in your component. so cc.valueChangedListener will call valueChangedListener method defined in YourComponentCC class.

Resources