How to use generated ids in EL (jsf, composite)? - jsf-2

I want to have a composite that composes
<h:form id="f_imgA" >
<h:graphicImage id="imgA"
onclick="document.getElementById('#{k_imgA.clientId}').value=mods(event)"
value="images/img?r=#{Math.random()}">
<f:ajax event="click" listener="#{mBean.handleEvent}"
execute="#this k_imgA" render="#this"></f:ajax>
</h:graphicImage>
<h:inputHidden id="k_imgA" binding="#{k_imgA}" value="#{mBean.keyX}" />
</h:form>
when I write
<comps:cimg imgId="imgA" />
The original purpose of this code is to send the modifier-states (Ctrl, Shift, Alt) to the server.
I have
<composite:interface>
<composite:attribute name="imgId" />
</composite:interface>
<composite:implementation>
<h:form id="f_#{cc.attrs.imgId}">
<h:graphicImage id="#{cc.attrs.imgId}"
onclick="document.getElementById('#{k_${cc.attrs.imgId}.clientId}').value=mods(event)"
value="images/img?r=#{Math.random()}">
<f:ajax event="click" execute="#this k_#{cc.attrs.imgId}"
listener="#{mBean.handleEvent}" render="#this">
</f:ajax>
</h:graphicImage>
<h:inputHidden id="k_#{cc.attrs.imgId}"
binding="k_#{cc.attrs.imgId}" value="#{mBean.keyX}" />
</h:form>
</composite:implementation>
which, quite expected, does not work. The offending expression is
#{k_${cc.attrs.imgId}.clientId}
which is intended to return the clientId of the hiddenInput with id k_imgA . As far as I know, EL cannot handle nested expressions like the one above, but it was worth a try. So is there a simple, straightforward way to get the clientId of k_imgA? I don't want to use more javascript, if this can be avoided.
Edit:
don't get confused about #{Math.random()}, it works just because I have a bean called "Math".
The javascript function mods is given by
<h:outputScript target="body">function mods(event) {return ((event.ctrlKey)?1:0)+((event.shiftKey)?2:0)+((event.altKey)?4:0)} </h:outputScript>

You don't need to fiddle with all those _#{cc.attrs.imgId}. Just give the composite a fixed id. They are already naming containers themselves. JSF will then worry about the rest.
<composite:implementation>
<h:form id="f">
<h:graphicImage id="i" ... />
<h:inputHidden id="k" ... />
</h:form>
</composite:implementation>
Usage:
<comps:cimg id="imgA" />
Generated HTML:
<form id="imgA:f" ...>
<img id="imgA:f:i" ... />
<input id="imgA:f:k" ... />
</form>
As to the JS attempt, you'd best use element's own ID as base:
<h:graphicImage onclick="document.getElementById(id.substring(0,id.lastIndexOf(':')+1)+'k').value='...'" />
Or, easier, if the hidden element is guaranteed to be the direct sibling of the image element in the generated HTML DOM tree, then just grab it by Node#nextSibling:
<h:graphicImage onclick="nextSibling.value='...'" />
<h:inputHidden ... />
The binding will never work as in your attempted construct, and even then you'd best do it via a Map in request scope or a so-called backing component.
See also:
Accessing JSF nested composite component elements in JavaScript
Integrate JavaScript in JSF composite component, the clean way
Rerendering composite component by ajax

Related

c:if inside input component and data table

I have a custom component that uses c:if to determine the ajax to use:
<composite:interface>
<composite:attribute name="value" required="true" />
<composite:attribute name="renderOnChange" type="java.lang.String"/>
</composite:interface>
<composite:implementation>
<h:inputText value="#{cc.attrs.value}">
<c:if test="#{not empty cc.attrs.renderOnChange}">
<a4j:ajax event="change" render="#{cc.attrs.renderOnChange}" />
</c:if>
<c:if test="#{empty cc.attrs.renderOnChange}">
<a4j:ajax event="change" render="#{somethingDifferent}" />
</c:if>
</h:inputText>
</composite:implementation>
The problem happens when I try to use the component in a data table because the c:ifs do not work. Is there a JSF equivalent that could be used to show the a4j:ajax depending on the attributes? I have tried ui:fragment, and a number of other ui tags to try to achieve this but none of them work inside the input component.
I would also like to use the same method to show f:validators.
Possible way is to create two <h:inputText> components then you can use the rendered attribute and place your condition from the <c:if> tags there.

f:param is not working with h:graphicImage

I'm getting <f:param> i.e., menuItemIndex value as null during my ajax call. Is it because it is with in <h:graphicImage> ??? Can any one suggest please? Note: Parameter is getting passed if I use <h:commandLink> instead of <h:graphicImage>.
<c:forEach items="#{cc.attrs.value}" var="menuItem" varStatus="loopItem">
....
<ui:fragment rendered="#{menuItem.hasChildren}">
<h:graphicImage library="images" name="#{menuItem.symbol}">
<f:ajax render=":fatcaForm:myMenu:menuID" event="click" listener="#{menuBean.refreshMenu}" />
<f:param name="menuItemIndex" value="{loopItem.count}" />
</h:graphicImage>
</ui:fragment>
....
</c:forEach>
The <h:graphicImage> is not a command component and it does therefore not support <f:param> children at all. The <f:param> works only when nested in an UICommand component, such as <h:commandLink> as you found out yourself.
So, this should do:
<h:commandLink action="#{menuBean.refreshMenu}">
<h:graphicImage library="images" name="#{menuItem.symbol}" />
<f:ajax render=":fatcaForm:myMenu:menuID" />
<f:param name="menuItemIndex" value="#{loopItem.count}" />
</h:commandLink>
(note that I fixed the EL syntax error in the <f:param value> as well)
Unrelated to the concrete problem, how you used the <h:graphicImage library> is not entirely right. Please carefully read What is the JSF resource library for and how should it be used?

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"

Composite component inside a ui:repeat, causes actionSource to not fire

OK, I'm guessing that this might be a bug but I'm not totally sure. Can anyone see what I'm doing wrong?
Here's the situation. I have a composite component that seems to work perfectly when it's alone on the page. The problem comes when the component is nested inside of a ui:repeat. Here are the two places I am calling the component (just for a test):
<q:case aCase="#{userSummaryBackingBean.userCases.get(0)}">
<f:setPropertyActionListener for="clickEvent" target="#{userSummaryBackingBean.value}" value="single"/>
<f:ajax execute="#this" render="#this :westPaneForm"/>
</q:case>
<ui:repeat value="#{userSummaryBackingBean.userCases}" var="aCase">
<li>
<q:case aCase="#{aCase}">
<f:setPropertyActionListener for="clickEvent" target="#{userSummaryBackingBean.value}" value="repeat"/>
<f:ajax execute="#this" render="#this :westPaneForm"/>
</q:case>
</li>
</ui:repeat>
and the component is defined as follows:
<cc:interface>
<cc:attribute name="aCase" type="com.autonomy.calltrack.data.dto.Case"/>
<cc:actionSource name="clickEvent" targets="caseCommandLink"/>
<cc:attribute name="action" targets="caseCommandLink" />
<cc:attribute name="actionSource" targets="caseCommandLink" />
<cc:clientBehavior name="click" event="action" targets="caseCommandLink" default="true"/>
</cc:interface>
<cc:implementation>
<h:commandLink id="caseCommandLink" styleClass="case ui-state-default" title="#{cc.clientId}">
--- more code ---
</h:commandLink>
</cc:implementation>
As you can see, the calls are exactly the same but the single item works and the items in the ui:repeat do not. Now, it makes sense to me that things will be problematic when the component itself has a ui:repeat. I mean, how would you retarget your actionSource, etc without knowing the ID of the items you need (which, inside of the repeat, is basically impossible).
HOWEVER, when I put the ui:repeat outside of the component I can't wrap my head around why things wouldn't work. The REALLY odd thing is that the f:ajax seems to work totally fine. VERY odd.
I've tried prefixing the targets attributes with :#{cc.clientId} and that doesn't help either (even though the "path" to the components is correct). Am I doing something wrong? Does anyone have a solution?
Alright, something must be wrong because even this doesn't work:
<ui:repeat value="#{userSummaryBackingBean.userCases}" var="aCase">
<h:commandLink id="caseCommandLink" value="test" styleClass="case ui-state-default">
<f:setPropertyActionListener target="#{userSummaryBackingBean.value}" value="REPEAT"/>
<f:ajax execute="#this" render="#this :westPaneForm"/>
</h:commandLink>
</ui:repeat>
What the heck could I be missing?
My page was using view params but they weren't a child of f:view so they were causing problems.
When I moved them everything started to work normally. I'm not sure why but it seems that things break when you have something like the following outside of an f:view:
<f:metadata>
<f:viewParam name="caseId" value="#{caseBackingBean.viewingCaseNumber}" />
</f:metadata>
<f:event type="preRenderView" listener="#{caseBackingBean.loadCase}" />
Thanks for the help BalusC.

Java facelets dynamic loading and composite component attributes

Currently I'm trying to implement webpart technology with JavaServer Faces 2.0 with Facelets view technology for educational purposes. I have created facelet templates, facelet custom components and made a few facelet "template" clients. But in one thing I'm stuck. I can't dynamically load controls and put them cc:attributes.
If I have a page for example with static text or bind it with ManagedBean the property ui:include everything works well.
<ui:define name="right-column">
right-column asd
<h:link outcome="asdf" value="link_get">
<f:param name="aa" value="123" />
<f:param name="a" value="123 dd + 20" />
</h:link>
<h:commandLink action="asdf?faces-redirect=true" value="asdf">
<f:param name="aa" value="123" />
</h:commandLink><br />
<ui:include src="./resources/Controls/CategoryTree.xhtml"/><br />
This works even if I put src with MenageBean property.
<ui:include src="#{browseProducts.incudePath}"/>
</ui:define>
Here is my facelet control (data binding is inside this control within #{TreeBean.root}:
<!-- NO INTERFACE -->
<cc:interface>
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
<!-- This is a very simply scaffolding for Category Three Control.
-->
<p:tree value="#{TreeBean.root}" var="node"
expandAnim="FADE_IN" collapseAnim="FADE_OUT">
<p:treeNode>
<h:outputText value="#{node}" />
</p:treeNode>
</p:tree>
</cc:implementation>
But I have problem when ui:include points to a control with cc:attribute. I don't know how to initialize this attribute from backing bean and do "stuff".
For example I have this page:
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets"
template="./resources/layout/Template.xhtml"
xmlns:sc="http://java.sun.com/jsf/composite/Controls">
<ui:define name="right-column">
<sc:dummy ItemCount="10" />
<ui:include src="./resources/Controls/dummy.xhtml" />
</ui:define>
</ui:composition>
Here goes the composite control:
<cc:interface>
<cc:attribute name="ItemCount" required="true"
shortDescription="This attribute is meaningful " />
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
<!-- How to pass ItemCount to my dummy bean to create so many items in
list as ItemCount value -->
<ui:repeat value="#{dummy.dummyList}" var="dummyItem">
<h:outputText value="#{dummyItem}" />
</ui:repeat>
</cc:implementation>
And backing bean code:
public ArrayList<String> getDummyList() {
//Here I try to get dummy list work.
dummyList = new ArrayList<String>(getDummyCount());
for (int i=0;i< getDummyCount();i++){
dummyList.add(i + "" + i);
}
return dummyList;
}
How can this be done?
I think you have two problems:
calling a method with parameter from a composite component
specifying some parameter to an included page
For 1., since jsf 2, you could call the method directly, specifying the parameter (which should be part of method signature):
<ui:repeat value="#{dummy.getDummyList(cc.attrs.dummyCode)}" var="dummyItem">
<h:outputText value="#{dummyItem}" />
</ui:repeat>
But I suspect you are trying to use a backing for something that it isn't designed for. Maybe you'll be interested in writing backing java code for your composite component, which is different. It's difficult to master if you are a beginner, though. I'd first try to design my pages and bean interactions differently. I don't know which problem you are trying to solve, but at first look, this solution looks too complicated.
For 2., you should have a look at ui:param. A quick google search gives me this: http://www.jsfcentral.com/articles/facelets_3.html

Resources