Why is not possible passing event name as argument to a composite component in JSF 2? - jsf-2

Why can't I pass event name as argument to a composite component bean in JSF 2.
This works:
<f:ajax event="click"
render="#{cc.attrs.ajaxRender}"
execute="#{cc.attrs.ajaxExecute}"
immediate="#{cc.attrs.immediate}"/>
This doesn't:
<f:ajax event="#{cc.attrs.itemEvent}"
render="#{cc.attrs.ajaxRender}"
execute="#{cc.attrs.ajaxExecute}"
immediate="#{cc.attrs.immediate}"/>
Question is: why?
NOTE: eventName is actually defined in cc:interface as the following:
<composite:attribute name="itemEvent" default="click"/>

Related

Passing parameter to a4j:ajax

We are upgrading from jsf 1.2 to jsf 2.
We are using apache myfaces 2.1 and rich faces 4.3.
Below is the xhtml code :
Prior to Migration :
<h:selectBooleanCheckbox id="comp1" value="#{bean.select}">
<a4j:support event="onclick" ajaxSingle="true" actionListener="#{bean.processInput}" reRender="compId">
<f:param name="name" value="paramValue"/>
</a4j:support>
</h:selectBooleanCheckbox>
We are passing one parameter with and accept the parameter in actionListener method as : (String)context.getExternalContext().getRequestParameterMap().get("name1");
After Migration :
<h:selectBooleanCheckbox id="comp1" value="#{bean.select}">
<a4j:ajax event="click" listener="#{bean.processInput}" execute="#this" render="compId"/>
</h:selectBooleanCheckbox>
I want to pass a parameter to bean.processInput method which has following signature :
public void processInput(AjaxBehaviorEvent event){
According to this post - Issues in passing parameter in f:ajax , we cannot use <f:param> (its not working
also) and we are not using EL 2.2 which rules out passing parameter in method signature.
Since we cannot use context.getApplication().evaluateExpressionGet due to constraints in our xhtml page only option available is
use <a4j:param name="" value="" assignTo=""/>.
But this needs to have one variable defined in bean requiring code change.
So my question is can we pass a parameter from UI to listener in second case and without changing the code.
You can use f:attribute like this :
<h:selectBooleanCheckbox id="comp1" value="#{bean.select}">
<a4j:ajax event="click" listener="#{bean.processInput}" execute="#this" render="compId"/>
<f:attribute name="name" value="paramValue" />
</h:selectBooleanCheckbox>
And use it in your bean :
public void processInput(AjaxBehaviorEvent event)
{
System.out.println(event.getComponent().getAttributes().get("name"));
}
Make sure that f:attribute component is not a child component of a4j:ajax.
it should be immediate child component of h:selectBooleanCheckBox, if not listener wont trigger.

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.

Unable to do ajax refresh from a composite component

Here is my composite component:
<composite:interface>
<composite:attribute name="attr1" />
<composite:clientBehavior name="xyz" event="valueChange" targets="chkbox" />
</composite:interface>
<composite:implementation>
<h:panelGrid>
<h:panelGroup>
<h:selectBooleanCheckbox id="chkbox"
value="#{cc.attrs.attr1}">
</h:selectBooleanCheckbox>
</h:panelGroup>
</h:panelGrid>
</composite:implementation>
And my page:
<h:form id="myForm">
<cc:myComp attr1="#{myBean.someAttr}">
<f:ajax render="myform:grid1" event="xyz" listener="{myBean.listenerMethod}"/>
</cc:myComp>
<h:panelGrid id="grid1">
<h:panelGroup>
<h:inputTextarea id="rationale" rows="4" cols="70"
value="#{myBean.rationale}" />
</h:panelGroup>
</h:panelGrid>
</h:form>
I'm getting below error:
<f:ajax> contains an unknown id 'myForm:grid1' - cannot locate it in the context of the component chkbox
If I remove render="myform:grid1" from my code then ajax call is working fine. Basically from my composite component I'm not able to refer another OUTSIDE component. How is this caused and how can I solve it?
<f:ajax render="myform:grid1">
Any client ID which does not start with the NamingContainer separator character, which is in your case :, is relative to the closest NamingContainer parent. The closest NamingContainer parent is in your particular case the composite component itself. So, this construct is basically looking for a component with the full client ID myform:XXX:myform:grid1 relative to the composite component, where XXX is the autogenerated ID of the composite component. However, there's no such component with this full client ID.
Assuming that the full client ID of the panel grid is indeed myform:grid1 (i.e. when you open the page in browser, rightclick and View Source, you're indeed seeing <table id="myform:grid1">), then you should make it an absolute client ID instead by prefixing it with the NamingContainer separator character:
<f:ajax render=":myform:grid1">
See also:
How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"

how can I validate a composite component on blur?

I have a composite component that contains two inputText components.
The composite component manages a bean containing two Integers.
class valueBean {
private Integer valueA;
private Integer valueB;
//getters, setters.
}
<composite:interface componentType="valueBeanUI">
<composite:attribute name="value">
</composite:interface>
<composite:implementation>
<h:inputText id="inputA"/>
<h:inputText id="inputB"/>
</composite:implementation>
In my valueBeanUI I implement getConvertedValue and encodeBegin.
Everything works as expected - when I click submit the code in getConvertedValue is called and I can throw error messages if the conversion values if the user enters characters in the integer fields.
What I would like to do is be able to require a valid value in both fields, and show one error message if this condition is not met. I have this working when the user clicks submit, but I'd like to fire it on blur of either field (inputA or inputB). I cant figure out how to validate the backing component when either field blurs. Can anyone suggest a way to achieve this? I dont want to submit the whole form - I'd like to do just this component via ajax.
Just add <f:ajax event="blur"> to the inputs.
<h:inputText id="inputA">
<f:ajax event="blur" execute="#this inputB" render="messageForInputA messageForInputB" />
</h:inputText>
<h:inputText id="inputB">
<f:ajax event="blur" execute="#this inputA" render="messageForInputA messageForInputB" />
</h:inputText>
The messageForInputA should point to a <h:message id="messageForInputA">.

Conditional rendering of subclasses within JSF ui:repeat tag

in a way similar to here I am using an abstract class to type the item set list
of ui:repeat. Concrete subclasses override the getType() method, that is used to
conditionally render the respective subtype with its particular properties:
<!-- AbstractAction Rule#getActions() -->
<ui:repeat value="#{rule.actions}" var="action">
<!-- render when "action" instance of NotificationAction -->
<h:panelGroup rendered="#{action.type == 'notification'}">
... UI for NotificationAction properties
<h:panelGroup rendered="#{action.type == 'callback'}">
...
When run on Glassfish 3 there is an error about properties not being defined on list
members of other subclasses (PropertyNotFoundException), which occurs in a branch that
is actually switched off by the rendered property. c:forEach/c:choose do not seem
appropriate. Any ideas how to make the rendering really conditional and bypass the
property checking are highly appreciated!
Thank you.
Jaro
after some testing it turned out, that the ui:repeat component itself caused the error.
Despite being in the final RenderResponse phase it tries to save the status of its child input components. Here a shortened exception dump:
Caused by: javax.el.PropertyNotFoundException: ... The class FOO does not have a readable property 'BAR'.
at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:104)
at com.sun.faces.facelets.component.UIRepeat.saveChildState(UIRepeat.java:343)
at com.sun.faces.facelets.component.UIRepeat.setIndex(UIRepeat.java:428)
at com.sun.faces.facelets.component.UIRepeat.process(UIRepeat.java:522)
at com.sun.faces.facelets.component.UIRepeat.encodeChildren(UIRepeat.java:926)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1613)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:273)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:127)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
Hereby the rendered condition is ignored and the EL interpreter complains about non-existent properties. There is a simple solution by using the h:dataTable iterator with a single column instead:
<h:dataTable value="#{rule.systemActions}" var="action">
<c:set var="name" value="#{action.class.simpleName.toLowerCase()}" />
<h:column>
<h:panelGroup rendered="#{name == 'notification'}">
<h:outputLabel for="phone">Phone:</h:outputLabel>
<h:inputText value="#{action.phone}" id="phone" />
</h:panelGroup>
<h:panelGroup rendered="#{name == 'reminder'}">
...
</h:panelGroup>
</h:column>
</h:dataTable>
Cheers.
Jaro

Resources