AJAX within nested JSF Composite Component - ajax

I'm having a bit of trouble getting AJAX calls to fire properly in a nested composite component with Mojarra 2.0.3 and PrimeFaces.
The child composite component looks something like this:
<cc:interface componentType="therapy">
<cc:attribute name="therapyType" type="java.lang.String" required="true"/>
<cc:attribute name="patientId" type="java.lang.String" required="true"/>
<cc:attribute name="showHistory" type="java.lang.Boolean" required="false" default="true"/>
<cc:attribute name="width" type="java.lang.String" required="false" default="350px"/>
<cc:attribute name="maxHistory" type="java.lang.String" required="false" default="3"/>
<cc:attribute name="collectDoctor" type="java.lang.Boolean" required="false" default="false"/>
<cc:attribute name="collectCareDate" type="java.lang.Boolean" required="false" default="false"/>
<cc:attribute name="important" type="java.lang.Boolean" requred="false" default="false"/>
</cc:interface>
<cc:implementation>
<script>
function #{cc.clientId}Toggle(){
$("##{cc.clientId}_newbutton").toggle();
$("##{cc.clientId}_savebuttons").toggle();
if(#{cc.attrs.collectDoctor}){
$("##{cc.clientId}_doctor").toggle();
}
if(#{cc.attrs.collectCareDate}){
$("##{cc.clientId}_care").toggle();
}
$("##{cc.clientId}_newTherapy").toggle(50);
}
function #{cc.clientId}rowHighlight(event){
if(event.status == 'begin'){
$("##{cc.clientId}_loader").toggle();
}
if(event.status == 'success'){
$("##{cc.clientId}\\:histTable tr:eq(1)").effect("highlight", {color:"#FED17A", easing:"easeInCubic"}, 2000);
}
}
$(function(){
if(#{cc.attrs.important}){
$("div[class~='ui-panel-titlebar'][id^='#{cc.clientId}']").css("background", "#FED17A");
}
});
</script>
<h:form prependId="false">
<p:panel styleClass="mcoPanel" style="width:#{cc.attrs.width};">
<f:facet name="header">
<h:panelGroup>
<span id="#{cc.clientId}_title">#{cc.myType.word}</span>
<span id="#{cc.clientId}_newbutton" class="mcoPanel-controls">
<span onclick="#{cc.clientId}Toggle();">
<h:graphicImage name="page_new.gif" library="images"/>
</span>
</span>
<span id="#{cc.clientId}_savebuttons" class="mcoPanel-controls" style="display:none;">
<span id="#{cc.clientId}_loader" style="display:none;">
<h:graphicImage name="ajax-loader.gif" library="images" height="16" width="16"/>
</span>
<h:commandLink action="#{cc.saveNewTherapy}">
<f:ajax execute="newOnTherapy newExemption newDoctor newCareDate" render="#form" onevent="#{cc.clientId}rowHighlight"/>
<h:graphicImage name="action_save.gif" library="images"/>
</h:commandLink>
<span onclick="#{cc.clientId}Toggle();">
<h:graphicImage name="page_cross.gif" library="images"/>
</span>
</span>
</h:panelGroup>
</f:facet>
<div id="#{cc.clientId}_newTherapy" class="mcoPanel-new" style="display:none;">
<h:outputLabel for="newOnTherapy" value="Satisfied:" style="position:relative; top: -10px;"/>
<p:selectOneMenu id="newOnTherapy" label="Satisfied" value="#{cc.newOnTherapyValue}" style="width: 60px;">
<f:selectItem itemLabel=""/>
<f:selectItems value="#{cc.yesNoList}"/>
</p:selectOneMenu>
<br/>
<h:outputLabel for="newExemption" value="Exemption:" style="position:relative; top: -10px;"/>
<p:selectOneMenu id="newExemption" value="#{cc.newExemption}" style="width: 150px;">
<f:selectItems value="#{cc.exemptions}"/>
</p:selectOneMenu>
<span id="#{cc.clientId}_doctor" style="display:none">
<br/>
<h:outputLabel for="newDoctor" value="Doctor:"/>
<p:inputText id="newDoctor" value="#{cc.newDoctor}"/>
</span>
<span id="#{cc.clientId}_care" style="display:none">
<br/>
<h:outputLabel for="newCareDate" value="Care Date:"/>
<p:calendar id="newCareDate" label="Care Date" value="#{cc.newCareDate}" showButtonPanel="true">
<f:converter converterId="dateOfBirthConverter"/>
</p:calendar>
</span>
</div>
<h:messages id="#{cc.clientId}_messages" errorClass="errorMessage"/>
<p:dataTable id="histTable" value="#{cc.history}" var="item" rendered="#{cc.attrs.showHistory}">
<!-- Table Output -->
</p:dataTable>
</p:panel>
</h:form>
And the parent composite component looks something like this:
<cc:interface>
<cc:attribute name="title" type="java.lang.String" required="true"/>
</cc:interface>
<cc:implementation>
<h:outputScript name="containerpanel.js" library="js/components" target="head"/>
<p:panel toggleable="true" styleClass="contentPanel" toggleSpeed="500" style="width:1100px;">
<f:facet name="header">
#{cc.attrs.title}
<div class="ui-panel-titlebar-icon ui-corner-all ui-state-default contentSaveAll">
Save All
</div>
<div class="ui-panel-titlebar-icon ui-corner-all ui-state-default contentExpandAll">
++
</div>
<div class="ui-panel-titlebar-icon ui-corner-all ui-state-default contentCollapseAll">
- -
</div>
</f:facet>
<cc:insertChildren>
<!-- Child components go here -->
</cc:insertChildren>
</p:panel>
</cc:implementation>
The implementation is supposed to allow for any number of child components inside the container component.
The child components send ajax requests and update their internal components perfectly if they are placed on a page outside of the Container component. In addition, if I use the same structure of the container, but not an actual composite component, everything works just fine.
As soon as I place the child components as children of the Container component, that's where things go wonky. No ajax calls in the child components fire properly (i.e. the save commandLink) Using firebug to help debug this, I've been able to determine the following:
The ajax post request appears to be being sent. The "begin" onevent is firing, and looking at the console in firebug shows a post being made to the server, with the proper component ids and values being sent back.
The action in the backing bean is not being called. I have a print statement as the first line and it's not printing.
The ajax request appears to be completing properly, the "success" onevent is firing.
Nothing is being updated on the page. The form does not refresh, nor does the p:messages object at the top of the page.
Obviously I can do this without the Container component, but I would prefer to be able to of course take advantage of code reuse. Am I missing something obvious here that I need to do to get the ajax to work properly?

I am having the exact same problem with MyFaces 2.1.6.
I can't make AJAX calls from nested Composite Components using ClientBehavior in the interface of the CC.
Like moneyT wrote the server is being notified of the events specified in the ajax tag, but when the lifecycle went to phase 5 - Invoke Application, the event was null and no listener was called. More and more it looks like a bug in the implementation of MyFaces.
EDIT: I have found another solution which is much better than hardcoding ajax in the composite component. I have refactored the outer composite component to be facelet. It loses some of the quirks of the composites but it gets the job done. Also it is reusable.
The only solution (more of a workaraund than solution) I found is to hardcode ajax in the composite component rather than to use ClientBehavior, like this:
<composite:implementation>
<h:panelGroup layout="block" id="listBox" class="list-box-scroll-pane" style="height:#{cc.attrs.visibleLines*27 + 5}px; width:#{cc.attrs.width}px">
<h:inputText id="submit-value" style="visibility: hidden; width:0px; height: 0px;" value="#{cc.attrs.property}">
<f:ajax event="change"
render="carModel"
listener="#{carDetailsMediator.changeCarMake}"
onevent="initComboboxes"/>
</h:inputText>
<ui:repeat value="#{cc.attrs.items}" var="element" varStatus="loop">
<input id="input-#{cc.attrs.customId}-#{loop.index}" type="hidden"
value="#{element.value}" />
<div id="div-#{cc.attrs.customId}-#{loop.index}" class="list-box-row">#{element.label}</div>
</ui:repeat>
</h:panelGroup>
</composite:implementation>
However this workaround is not very good, because the idea behind composite components is that you can reuse them with many different options. Hardcoding ajax in it kind of limits the options.

I believe your problem is this:
<f:ajax execute="newOnTherapy newExemption newDoctor newCareDate" ...
The execute attribute can refer to the special keywords (Eg. #this, #form, etc...) or according to the documentation:
If a literal is specified, it must be a space-delimited String of component identifiers and/or one of the keywords.
When you are inserting this component as a child to the parent component then the component Ids are going to by dynamically determined and can't be referred to absolutely.

Related

p:tree ajax events are updating other components

I have a problem with ajax events of the p:tree component
Here's the xhtml code:
<form id="form1">
<!-- Other components -->
<p:panel id="panel1" binding="#{panel1}"
header="..." toggleable="true" toggleSpeed="500"
collapsed="false" dynamic="true"
rendered="#{service.checkMethod(...)}">
<div class="column">
<p:tree id="treeId" value="#{tree.root}"
var="node" selectionMode="single"
rendered="#{tree != null}"
selection="#{flowScope.selectedNode}"
widgetVar="treeId"
process="tree">
<p:ajax event="select" process="#this"
update="#this :#{anotherPanel.clientId} :#{anotherPanel2.clientId}"
listener="#{service.onNodeSelect}" oncomplete="setSelectedNode()" />
<p:ajax event="expand" process="#this" listener="#{service.onNodeExpand}" />
<p:ajax event="collapse" process="#this" listener="#{service.onNodeCollapse}" />
<p:treeNode>
<h:outputLabel value="#{node.attribute}" />
</p:treeNode>
</p:tree>
<h:outputLabel
value="Empty"
rendered="#{tree.isEmpty()}" />
</div>
</p:panel>
<!-- Other components -->
</form>
The panel1 rendered attribute has a method which will create a new tree every time is updated, but somehow all the ajax events on the tree are calling that method. Even the expand and collapse update, which doesn't have the update defined, is doing that.
I already tried putting global="false", immediate="true", using different type of updates and process, on all the ajax events, or putting other panels between the tree and the panel, but it has the same effect. I searched all my files with Eclipse, to make sure that this method isn't called elsewhere, but it's only used here.
Is there a way to prevent the tree ajax events from re-rendering the above panel?

how i get clicked CommandLink id in Controller in JSF?

<ui:repeat value="#{prodCtr.paginator.model}" var="o">
<h:form>
<h:commandLink id="#{o.id}" action="ModelInfo.xhtml" actionListener="#{prodCtr.listener}">
<h:panelGrid columns="1" style="border: #ffffff">
<img src="resources/images/#{o.id}.jpg" style="width: 100px;height: 100px"/>
<h:outputLabel value="#{o.price} YTL" style="font-size: 12px;font-family: Georgia, Serif;color: #ff3300" />
<h:commandButton value="Sifaris et" class="button"/>
</h:panelGrid>
</h:commandLink>
</h:form>
</ui:repeat>
java.lang.IllegalArgumentException caught during processing of RENDER_RESPONSE 6 : UIComponent-ClientId=, Message=Empty id attribute is not allowed here
How can i CommandLink id dinamically?Is there an other way?
You don't need to specify the id attribute for the child components when using the <ui:repeat/> tag. It automatically does this.
If you do want to be in control of the id attribute, <ui:repeat/> is the wrong one to use for that. The reason is that at point in time that the components are being placed in the UIViewroot (essentially when the page is being constructed for the first time), the <ui:repeat/> component is not active, i.e. it's not being processed. So the var="o" is not available to the runtime and so nothing is available to be used as id.
To be in control of the id, you should be using the <c:forEach/> tag instead. Your code should look like this:
<c:forEach items="#{prodCtr.paginator.model}" var="o">
<h:form>
<h:commandLink id="#{o.id}" action="ModelInfo.xhtml" actionListener="#{prodCtr.listener}">
<h:panelGrid columns="1" style="border: #ffffff">
<img src="resources/images/#{o.id}.jpg" style="width: 100px;height: 100px"/>
<h:outputLabel value="#{o.price} YTL" style="font-size: 12px;font-family: Georgia, Serif;color: #ff3300" />
<h:commandButton value="Sifaris et" class="button"/>
</h:panelGrid>
</h:commandLink>
</h:form>
</c:forEach>

JSF passing object to template

I have two jsf pages. layout.xhtml and page.xhtml. Layout looks like this:
<ui:composition>
<h:panelGroup id="menu" layout="block">
<h:outputText value="#{menuBean}" />
<h:form>
<ui:repeat var="menuItem" value="#{menuBean.menuItems}">
<button:menuItem label="#{msgs[menuItem.label]}" action="#{menuBean.selectItem(menuItem.label)}" update="#{update}" />
</ui:repeat>
</h:form>
</h:panelGroup>
<ui:repeat var="menuItem" value="#{menuBean.menuItems}">
<h:panelGroup layout="block" rendered="#{menuBean.selectedItemLabel eq menuItem.label}">
<ui:include src="#{menuItem.page}" />
</h:panelGroup>
</ui:repeat>
And page like this:
<h:panelGroup binding="#{page}" layout="block">
<ui:decorate template="../template.xhtml">
<ui:param name="menuBean" value="#{pageBean}" />
<ui:param name="update" value=":#{page.clientId}" />
</ui:decorate>
</h:panelGroup>
When I first time render page everything is ok (menuItems are rendered etc). After I click any button I get Target Unreachable, identifier 'menuBean' resolved to null.
Can anyone explain me what is happening why the menuBean isn't assigned again and if there exists another way to achieve this kind of thing (to have some generic layout page, pass some object there and generate page)? My beans are managed by Spring.
UPDATE:
I guess the problem is somehow connected to my composite button which looks like this:
<composite:interface name="menuItem">
<composite:attribute name="action" targets="button" />
<composite:attribute name="styleClass" />
<composite:attribute name="label" />
<composite:attribute name="update" />
<composite:attribute name="rendered" />
<composite:insertChildren />
</composite:interface>
<composite:implementation>
<h:commandButton id="button" value="#{cc.attrs.label}" style="width: 150px;" action="#{cc.attrs.action}"
rendered="#{empty cc.attrs.rendered ? true : cc.attrs.rendered}" styleClass="menu-item #{cc.attrs.styleClass}" type="button">
<f:ajax render="#{cc.attrs.update}" />
</h:commandButton>
</composite:implementation>
If I replace the tag by standard h:commandButton everything works perfectly. I am passing object into template and the template is passing the passed object into composite, but I am definitely missing something.
I think the problem is connected with the scope of the beans. As I understand your pageBean backing bean have request scope. Try to change scope on wider one (it should be view scope #ViewScoped). If you use CDI annotations instead of JSF then you can look at #ConversationalScope (or you can use MyFacesCODI library as Luiggi noted).
The problem I am facing to BalusC already described on his blogspot:
http://balusc.blogspot.cz/2011/09/communication-in-jsf-20.html#ViewScopedFailsInTagHandlers
Simply put at the moment of postback request (ajax refresh of button) bean passed into my composite button does not exist. As BalusC noted, solution can be creating of standard UIComponent instead of using composite shortcut.

JSF <h:commandButton> with <f:ajax> not executed on #ViewScoped bean but <a4j:ajax> works

I have a Facelets subview at /subviews/document-tree.xhtml that renders a tree for each tab on a <rich:tabPanel> client. The page and sub views are based on JSF 2 and RichFaces 4.
<ui:composition ...>
<rich:tree value="#{rootNode}" var="treeNode" id="#{treeId}">
<rich:treeNode ... id="chapternode">
<h:panelGrid columns="2">
<rich:outputText value="#{treeNode.name}" />
<h:commandLink>
<h:graphicImage library="images/icons" name="delete.png" />
<rich:componentControl target="remove-chapter-popup" operation="show" />
</h:commandLink>
<rich:popupPanel modal="true"
onmaskclick="#{rich:component('remove-chapter-popup')}.hide(); return false;"
id="remove-chapter-popup">
<f:facet name="header">
<h:outputText value="Remove chapter?" />
</f:facet>
<f:facet name="controls">
<h:outputText value="X" />
</f:facet>
<p>Remove chapter #{treeNode.name}?</p>
<h:panelGrid columns="2">
<h:commandButton value="Add"
action="#{nodeManager.removeChapterNode(treeNode)}"
onclick="#{rich:component('remove-chapter-popup')}.hide(); return true;">
<!--f:ajax execute="#this" render="#form" /--> <!-- never executed! -->
<a4j:ajax execute="#this" render="#form" /> <!-- this works however! -->
</h:commandButton>
<h:commandButton value="Cancel"
onclick="#{rich:component('remove-chapter-popup')}.hide(); return false;" immediate="true" />
</h:panelGrid>
</rich:popupPanel>
</h:panelGrid>
</rich:treeNode>
...
</rich:tree>
</ui:composition>
This basically shows tree nodes with their name plus an image to the right for deletion.
Each tree sub view is placed into a <rich:tab>, so the tab panel does have the required enclosing <h:form>. There are no other nested forms (forbidden anyway).
The #{nodeManager.removeChapterNode(treeNode)} bean was correctly marked as #ViewScoped.
Now what happens is kinda strange:
When using <f:ajax execute="#this" ... /> the button never executes, whereas using <a4j:ajax execute="#this" ... /> always works.
Why? What's wrong here?
It doesn't make much sense, given the fact that RichFaces <a4j:ajax> is based 100% on JSF 2 <f:ajax> according to their own words.
Could it be a bug in JSF 2.1.7, which I'm using? (the implementation that came with JBoss AS 7.1.1.Final)
Spaces are illegal in IDs. See also UIComponent#setId() javadoc.
setId
public abstract void setId(java.lang.String id)
Set the component identifier of this UIComponent (if any). Component identifiers must obey the following syntax restrictions:
Must not be a zero-length String.
First character must be a letter or an underscore ('_').
Subsequent characters must be a letter, a digit, an underscore ('_'), or a dash ('-').
Component identifiers must also obey the following semantic restrictions (note that this restriction is NOT enforced by the setId() implementation):
The specified identifier must be unique among all the components (including facets) that are descendents of the nearest ancestor UIComponent that is a NamingContainer, or within the scope of the entire component tree if there is no such ancestor that is a NamingContainer.
Parameters:
id - The new component identifier, or null to indicate that this UIComponent does not have a component identifier
Throws:
IllegalArgumentException - if id is not syntactically valid
Seems like that RichFaces is never validating it according the rules for the tree. I would in turn account it as a bug in RichFaces. Report it to the RichFaces guys.
Here's the reduced diff:
<form id="tree-form" name="tree-form" method="post" action="/pqgenerator2/debug.jsf" enctype="application/x-www-form-urlencoded">
...
<table style="margin: 0 auto;">
<tbody>
<tr>
- <td><input id="tree-form:sorting-tree-one:real root:j_idt34" type="submit" name="tree-form:sorting-tree-one:real root:j_idt34" value="Fortfahren" onclick="jsf.util.chain(this,event,'RichFaces.$(\'tree-form:sorting-tree-one:real root:add-root-chapter-popup\').hide(); return true;','mojarra.ab(this,event,\'action\',\'#this tree-form:sorting-tree-one:real root:new-root-chapter-name-input\',\'#form\')');return false" /></td>
+ <td><input id="tree-form:sorting-tree-one:real root:j_idt34" type="submit" name="tree-form:sorting-tree-one:real root:j_idt34" value="Fortfahren" onclick="jsf.util.chain(this,event,'RichFaces.$(\'tree-form:sorting-tree-one:real root:add-root-chapter-popup\').hide(); return true;','RichFaces.ajax(this,event,{"parameters":{"javax.faces.behavior.event":"action","org.richfaces.ajax.component":"tree\\u002Dform:sorting\\u002Dtree\\u002Done:real root:j_idt34"} ,"sourceId":this} )');return false" /></td>
</tr>
</tbody>
</table>
...
- </div></span></span></div></div><input type="hidden" name="tree-form:sorting-tree-one__SELECTION_STATE" id="tree-form:sorting-tree-one__SELECTION_STATE" class="rf-tr-sel-inp" value="" /><script type="text/javascript">new RichFaces.ui.Tree("tree\u002Dform:sorting\u002Dtree\u002Done",{"toggleType":"client"} );</script></div></div><script type="text/javascript">new RichFaces.ui.Tab("tree\u002Dform:j_idt21",{"index":0,"leave":null,"togglePanelId":"tree\u002Dform:tree\u002Dtabpanel","switchMode":"client","name":"Blah GmbH","enter":null,"disabled":false} )</script></div><script type="text/javascript">new RichFaces.ui.Tab("tree\u002Dform:j_idt21",{"index":0,"leave":null,"togglePanelId":"tree\u002Dform:tree\u002Dtabpanel","switchMode":"client","name":"Blah GmbH","enter":null,"disabled":false} )</script></div><input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="998210192617713914:-9142017502724223608" autocomplete="off" />
+ </div></span></span></div></div><input type="hidden" name="tree-form:sorting-tree-one__SELECTION_STATE" id="tree-form:sorting-tree-one__SELECTION_STATE" class="rf-tr-sel-inp" value="" /><script type="text/javascript">new RichFaces.ui.Tree("tree\u002Dform:sorting\u002Dtree\u002Done",{"toggleType":"client"} );</script></div></div><script type="text/javascript">new RichFaces.ui.Tab("tree\u002Dform:j_idt21",{"index":0,"leave":null,"togglePanelId":"tree\u002Dform:tree\u002Dtabpanel","switchMode":"client","name":"Blah GmbH","enter":null,"disabled":false} )</script></div><script type="text/javascript">new RichFaces.ui.Tab("tree\u002Dform:j_idt21",{"index":0,"leave":null,"togglePanelId":"tree\u002Dform:tree\u002Dtabpanel","switchMode":"client","name":"Blah GmbH","enter":null,"disabled":false} )</script></div><input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="-5805340602741883884:1908800949269113937" autocomplete="off" />
</form>
The problem here is that I create a dummy root node for the RichFaces root to be displayed and I add the real root via RichFaces TreeNodeImpl's addChild("real root", ...), which contains a space in the key.
The <a4j:ajax> code can obviously handle this but not JSF 2's <f:ajax> (note the first diff part).

f:ajax works only with #form or #all

i have a composite component that contains a form with ajax. what i can't figure out is why it only works for the #form/#all render parameter.
<cc:interface>
<cc:attribute name="radioItem"/>
<cc:attribute name="radioItems" />
<cc:attribute name="compImage" />
<cc:attribute name="compSpecification"/>
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
<h:form>
<div id="options">
<h:selectOneRadio value="#{cc.attrs.radioItem}" >
<f:selectItems value="#{cc.attrs.radioItems}" />
<f:ajax render=":details" />
</h:selectOneRadio>
</div>
<div id="details">
<h:graphicImage value="#{cc.attrs.compImage}"/>
<h:outputText value="#{cc.attrs.compSpecification}"/>
</div>
</h:form>
</cc:implementation>
What am i missing?
This problem is two-fold:
You're specifying an absolute client ID :details while the element to be re-rendered is inside the same UINamingContainer parent, so JSF won't be able to locate it in the component tree. You need to specify a relative client ID details.
You have specified the ID on a plain vanilla HTML element instead of a real JSF component, so JSF won't be able to locate it in the component tree. You need to use a real JSF component. You need to replace <div> by <h:panelGroup layout="block">.
So, fix it both:
<h:form>
<div id="options">
<h:selectOneRadio value="#{cc.attrs.radioItem}" >
<f:selectItems value="#{cc.attrs.radioItems}" />
<f:ajax render="details" />
</h:selectOneRadio>
</div>
<h:panelGroup id="details" layout="block">
<h:graphicImage value="#{cc.attrs.compImage}"/>
<h:outputText value="#{cc.attrs.compSpecification}"/>
</h:panelGroup>
</h:form>
That it works with #form and #all is just because JSF can locate the element to be re-rendered relative to the component on which the ajax action is been invoked, namely the parent UIForm and UIViewRoot component respectively.

Resources