Here is my (simplified) issue :
I've got a page that is using 2 composite components of mine :
- CCSelection
- CCDisplay
In CCSelection, I have a list of values, each one has got a h:commandLink onto.
When clicking on a link, the CCDiaplay component is refreshed using the selected value.
To do this, CCSelection exposes a method attribute that is directly linked on each h:commandLink. The value is given to the method using f:attribute.
In the page backing bean, I've got a method (that is given to CCSelection as an attribute), that sets a member.
CCDisplay gets this value though an cc:attribute via the pages's member's getter.
It works !
Now, I want to ajaxize this behaviour.
I tryed to put an f:ajax for each h:commandLink in CCSelection... but if I put @form or @all in the render attribute, nothing is rendered (but the setter methods are called). If I put the id of the UIComponent (of the Page) to render, I get a nullpointerexception saying that a property is not defined for NamingContainer in CCDisplay. Quite strange because I didn't change anything inside CCDisplay !
I think the solution is to put the f:ajax not inside CCSelection but in Page.
So there may be 2 ways to achieve this :
- CCSelection raises an event f:ajax can manage... but how ?
- Using cc:clientBehaviour for CCSelection. But is it possible to target more that 1
component (I've got many h:commandLink, but I want only 1 event).
- Other ways ?
Here is a pseudo code
page.xhtml
<myComp:ccSelection actionMethod="#{pageBean.select}"
render="#{clientIDHelper.clientId['display']}" />
<h:panelGroup id="diplay" binding="#{clientIDHelper.bindings['display']}">
<myComp:ccDisplay value="#{pageBean.value}" />
</h:panelGroup>
To recover the full clientid of the panel containing the ccDiaplay composite component, I use a clientIDMap technic described here.
PageBean.java
private String _value;
public String getValue() { return _value; }
public void setValue(String value) [ _value = value; }
public void select(String value) {
setValue(value);
}
ccSelection.xhtml
<cc:interface>
<cc:attribute method-signature="void selectAction(String)"
name="actionMethod" />
<cc:attribute name="render" />
</cc:interface>
<cc:implementation>
<t:dataTable value="#{cc.values}"
var="val"
...
>
<h:column>
<t:commandLink actionListener="#{cc.selectionValueListener}"
<f:ajax render="#{cc.attrs.render}" />
<f:attribute name="value"
value="#{val}" />
</t:commandLink>
</h:column>
</t:dataTable>
</cc:implementation>
ccSelection.java
public void selectionValueListener() {
// recover the attribute value
String value = event.getComponent().getAttributes().get("value");
// call the callback method of the page
FacesContext context = FacesContext.getCurrentInstance();
MethodExpression method = (MethodExpression) this.getAttributes().get("actionMethod");
if (method != null)
method.invoke(context.getELContext(), new Object[] {value});
}
I don't think ccDisplay is interressting.
So, if I don't put the f:ajax tag, it works.
When I put the f:ajax with the render pointing to the clientId passed in param, I get an error while loading the page.
If I change the render for @form or @all, the pageBean.select method is called, but ccDisplay is not refreshed.
OK. It is solved... but I don't like it very much.
You'll think I'm a fool : I solved the problem by removing the <![CDATA surrounding my scripts !
I've already found some issue using CDATA. I don't know if this is a MyFaces bug or something I do the wrong way like putting many h:outputScript blocks with CDATA in composite components but with CDATA, I get errors or not working. Just removing it, it works !