how i get clicked CommandLink id in Controller in JSF? - jsf-2.2

<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>

Related

Contents of ui:repeat not updated with p:fileUpload

I am running a JSF2 web application with PrimeFaces 3.5 on GlassFish 3.1.2. I want to implement CSV import functionality, so that the read values are filled in to the components without page reload.
Technically, after a file upload with <p:fileUpload>, I want to update a component which creates the input fields iteratively with <ui:repeat>. Here is the simplified code of my view:
<ui:define name="content">
<h:form id="form" prependId="false" enctype="multipart/form-data">
<p:growl id="growl" showDetail="true" life="5000" />
<p:panel header="Values">
<h:panelGroup id="grid" layout="block">
<p:commandButton actionListener="#{table.handlePost}"
id="postTop" value="Save" update="growl" />
<p:commandButton id="dlTop" value="Export CSV" ajax="false"
onclick="PrimeFaces.monitorDownload(start, stop)"
icon="ui-icon-arrowthichk-s">
<p:fileDownload value="#{table.csv}" />
</p:commandButton>
<p:fileUpload fileUploadListener="#{table.handleFileUpload}"
mode="advanced" update="tableView, growl" sizeLimit="10000"
allowTypes="/(\.|\/)(csv)$/" label="Import CSV"
showButtons="false" />
<h:panelGroup id="tableView" layout="block">
<table>
<tr>
<th>Day</th>
<th>Value</th>
</tr>
<ui:repeat value="#{table.values}" varStatus="val" id="table">
<tr>
<td><h:outputText
value="#{table.date}-#{val.index+1}" /></td>
<td><h:inputText id="value"
value="#{table.values[val.index]}"
converter="javax.faces.Double" /></td>
</tr>
</ui:repeat>
</table>
</h:panelGroup>
<p:commandButton actionListener="#{table.handlePost}"
id="postBottom" value="Save" update="growl" />
<p:commandButton id="dlBottom" value="Export CSV" ajax="false"
onclick="PrimeFaces.monitorDownload(start, stop)"
icon="ui-icon-arrowthic hk-s">
<p:fileDownload value="#{table.csv}" />
</p:commandButton>
</h:panelGroup>
</p:panel>
</h:form>
</ui:define>
table bean:
#ViewScoped
#ManagedBean(name="table")
public class TableView { }
Values are read correctly, I can update any component to output table.values, except with <p:fileUpload>, contents of the <ui:repeat> are never updated. If I update tableView from any other component other than <p:fileUpload>, the contents of <ui:repeat> are updated correctly. What could be the problem?
I don't think the file upload component supports the ajax update tag, so you may have to do a full page refresh...you could do this in javascript by using: oncomplete="javascript:window.location.reload(true)"

AJAX within nested JSF Composite Component

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.

Jsf2 ajax render id not found

Why i get " contains an unknown id ':commentTextArea' - cannot locate it in the context of the component commentLink" error?
majorra 2.1.7
How i can render commentTextArea.
<ui:repeat var="parentComment" value="#{commentTree.parentCommentsWrapper}"> ....
<h:form>
<h:commandLink id="commentLink" value="comment"
style="height:20px;width:20px;padding: 0; margin: 0;"
action="#{parentComment.changeEditable}">
**<f:ajax render=":commentTextArea"></f:ajax>**
</h:commandLink>
</h:form> <h:panelGroup id="commentTextArea">
<br />
<h:panelGroup rendered="#{parentComment.editable}">
<h:form >
<h:inputTextarea rows="2" cols="20"
value="#{commentTree.newCommentText}"
style="font-family:Arial,Helvetica;font-size:12px;width:365px;margin-right: 4px;margin-left: -1px;">
</h:inputTextarea>
<h:commandButton id="newCommentButton" value="+"
style="height:20px;width:20px;padding: 0; margin: 0;"
action="#{commentTree.saveChildComment(parentComment.comment)}" >
</h:commandButton>
</h:form>
</h:panelGroup>
</h:panelGroup>
</ui:repeat>
'
The id :commentTextArea is an absolute id and relative to the view root. However, ui:repeat is a NamingContainer and "wraps" your commentTextArea. (Check the html source in browser to see what id is generated)
A simple solution would be to use only one form in your 'ui:repeat'. Then the relative id with
<f:ajax render="commentTextArea">
(without :) would work.

Ajax not working with primefaces

In my JSF page I have form when I click submit button in the same page I need to show an static content. But When I click <p:commandbutton> nothing appears in the page. When I put ajax="false" then only action taken place but the Static content display in new page.
In my Backing bean I have used Session scoped. I am using JSF 2.0 and Primefaces 3.2.
My JSF Page.
<h:form>
<h:panelGroup rendered="#{not license.showForm}">
<p:panel header="License Key Request" >
<h:panelGrid columns="2" >
..
<h:outputText value="Bla bla"/>
..
<h:outputText value="Bla Bla" />
</h:panelGrid>
<TABLE>
..
<h:outputLabel for="name" value="Requestor Name : *" />
..
<p:inputText id="name" value="#{license.name}" required="true" requiredMessage="Name is required."/>
..
<h:outputLabel for="company" value="Company : * " />
..
<p:inputText id="company" value="#{license.company}" required="true" requiredMessage="Company is required."/>
..
</table>
<p:commandButton value="Form" id="btnAdd" process="#form"
action="#{license.add}" />
..
</table>
</p:panel>
</h:panelGroup>
<h:panelGroup rendered="#{license.showForm}" >
<h:outputText value="Bla Bla"/>
</h:panelGroup>
</h:form>
Your source code is somewhat confusing (e.g. you have two closing table tags). But I assume that you want to show the panelGroup at the bottom if the button is clicked.
The easiest way would be to add an update ="#form" to your commandButton:
<p:commandButton value="Form" id="btnAdd"
process="#form" update="#form"
action="#{license.add}" />
You don't need to update the whole form but only the specific component. Then you need to give the panelGroup an id attribute and use this attribute instead of #form. However, since it is not clear for me how your naming containers are organized, it could be puzzling to find the correct relative id.

Click event on div in JSF2

I have a list represented by a ui:repeat, where each item is a div with some data. What I want is to show the item detail by clicking on the div.
I made it with a button refreshing the form but can't make it work with onclick event on panelGroup.
<h:panelGroup styleClass="container" layout="block">
<ui:repeat value="#{bean.model}" var="item">
<h:panelGroup styleClass="item_data" layout="block">
ID: #{item.id}
<h:commandButton action="#{bean.select}" value="Select item">
<f:param name="itemId" value="#{item.id}"/>
<f:ajax render="#form"></f:ajax>
</h:commandButton>
</h:panelGroup>
<h:panelGroup styleClass="detail"
layout="block"
rendered="#{item eq bean.selected}">
value: #{bean.selected.value}
</h:panelGroup>
</ui:repeat>
</h:panelGroup>
If I understand you correctly, you just want to show the detail section without re-rendering the whole form? If so, then wrap it in a component which is always rendered in the HTML DOM tree, give it an id and refer it instead:
<ui:repeat value="#{bean.model}" var="item">
<h:panelGroup styleClass="item_data" layout="block">
ID: #{item.id}
<h:commandButton action="#{bean.select}" value="Select item">
<f:param name="itemId" value="#{item.id}"/>
<f:ajax render="detail" />
</h:commandButton>
</h:panelGroup>
<h:panelGroup id="detail">
<h:panelGroup styleClass="detail"
layout="block"
rendered="#{item eq bean.selected}">
value: #{bean.selected.value}
</h:panelGroup>
</h:panelGroup>
</ui:repeat>
See also:
Communication in JSF 2.0 - Ajax rendering of content which is by itself conditionally rendered
Update: as per the comments, you actually want to invoke JSF ajax action by <h:panelGroup onclick> no, that's not possible as it doesn't support onclick attribute. You could use a <div> with jsf.ajax.request() function, but cleaner would be to use a <h:commandLink> with CSS display: block so that it spans the entire div.
<h:panelGroup styleClass="item_data" layout="block">
<h:commandLink action="#{bean.select}" value="ID: #{item.id}" styleclass="detailLink">
<f:param name="itemId" value="#{item.id}"/>
<f:ajax render="detail" />
</h:commandLink>
</h:panelGroup>
with e.g.
.detailLink {
display: block;
color: black;
text-decoration: none;
}

Resources