f:ajax inside ui:repeat - jsf

I need to show a list of auction items. When a user clicks on a Bid button next to each item, I'd like to have ajax open a bid form right under this auction item. So I'm going with a ui:repeat and a f:ajax as shown below, but when I go to the page all the auction items have the bid component open next to them. And clicking any of the buttons doesn't do anything. This is the code (with the bid form simplified to just an outputText:)
<h:form>
<table border="1">
<ui:repeat var="parcel" varStatus="status" value="#{auctionsViewBean.parcels}">
<tr>
<td><h:commandButton value="Bid" action="nothing">
<f:ajax render="bidView"/>
</h:commandButton></td>
<td>#{status.index + 1}</td>
<td>#{parcel.a}</td>
<td>#{parcel.b}</td>
<td>#{parcel.c}</td>
</tr>
<tr><td><h:outputText id="bidView" value="#{auctionsViewBean.showBidViewForParcel(parcel)}">Some text</h:outputText></td></tr>
</ui:repeat>
</table>
</h:form>
What am I doing wrong? And how can I specify only the bid component related to the clicked auction item?

If I understand you correctly, you want to initially hide the bidView until the button is pressed? You can do it by giving it a rendered attribute and put it in another component which can be referenced by <f:ajax>. You only need to rearrange the action method and checking logic.
<h:commandButton value="Bid" action="#{auctionsViewBean.addBidView(parcel)}">
<f:ajax render="bidView" />
...
<h:panelGroup id="bidView">
<h:panelGroup rendered="#{auctionsViewBean.showBidView(parcel)}">
...
</h:panelGroup>
</h:panelGroup>
with something like this:
public void addBidView(Parcel parcel) {
bidViews.put(parcel, new BidView());
}
public boolean isShowBidView(Parcel parcel) {
return bidViews.containsKey(parcel);
}

Related

Primefaces Dialog doesnt show on oncomplete [duplicate]

I have a JSF 2 application that has two pages, one to list students and one to show details of a given student. The listing page has a link to the details page in each row of the students table, that opens a new tab in browser to show those details, when clicked.
Now the requirements changed to no more show details in a new tab, but in a modal dialog in the listing page.
My idea is to simply embed the details page content in the modal dialog so the listing page will not get too big and hard to maintain. Here start my doubts. After some research I changed the link in each row of the listing to the following button:
<p:commandButton value="Details" type="button"
onclick="PF('dialog-details').show()">
</p:commandButton>
The dialog is declared as follows:
<p:dialog widgetVar="dialog-details" header="Details" modal="true" width="95%">
<ui:include src="student_details.xhtml">
<ui:param name="id" value="#{student.id}"/>
</ui:include>
</p:dialog>
Finally, the details page was changed to be something like this:
<ui:composition
xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui" xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<f:metadata>
<f:viewParam name="id" value="#{studentBean.id}" />
</f:metadata>
<h1 class="title ui-widget-header ui-corner-all">Details of #{studentBean.bean.name} / #{studentBean.bean.number}</h1>
</ui:composition>
When I click the button, the dialog really shows and the content is the details page. I see the following content in the dialog:
Details of /
No errors at all, but the data that should be shown, isn't. A breakpoint was set in StudentBean.setId() (this method loads a property named bean with the Student instance corresponding to the passed id) but it is never hit.
After some time thinking about it, I came to understand why it does not work. The parameter passed to the details page is student.id, but student is the name used as the var in the <p:datatable/> that show all the students, so student is not valid in <p:dialog/> which is outside the <p:datatable/>.
So, what I need is a way to show the dialog using the id of the corresponding student in a given row. Ideally, I would like an ajax call here, so the details would loaded only when neded.
Any ideas?
The button should be an ajax button which sets the currently iterated entity in the bean, and then updates the dialog's content, and finally shows it. The dialog should just reference that entity in the bean and update the list and table on save. It's very important that dialog is placed outside the main form and that it has its own form.
Here's a kickoff example:
<h:form id="master">
<p:dataTable value="#{bean.entities}" var="entity">
<p:column>#{entity.property1}</p:column>
<p:column>#{entity.property2}</p:column>
<p:column>#{entity.property3}</p:column>
...
<p:column>
<p:commandButton value="View" action="#{bean.setEntity(entity)}"
update=":detail" oncomplete="PF('detail').show()" />
</p:column>
</p:dataTable>
</h:form>
<p:dialog id="detail" widgetVar="detail">
<h:form>
<p:inputText value="#{bean.entity.property1}" />
<p:inputText value="#{bean.entity.property2}" />
<p:inputText value="#{bean.entity.property3}" />
...
<p:button value="Close" onclick="PF('detail').hide(); return false" />
<p:commandButton value="Save" action="#{bean.save}"
update=":master" oncomplete="if(!args.validationFailed) PF('detail').hide()" />
</h:form>
</p:dialog>
With this inside a #ViewScoped bean:
private List<Entity> entities; // +getter
private Entity entity; // +getter+setter
#EJB
private EntityService entityService;
#PostConstruct
public void load() {
entities = entityService.list();
entity = null;
}
public void save() {
entityService.save(entity);
load();
}
See also:
Creating master-detail pages for entities, how to link them and which bean scope to choose
Creating master-detail table and dialog, how to reuse same dialog for create and edit
Keep p:dialog open when a validation error occurs after submit
Difference between rendered and visible attributes of <p:dialog>
How to display dialog only on complete of a successful form submit

Pass object from <p:dataTable> to <p:dialog> JSF/PrimeFaces [duplicate]

I have a JSF 2 application that has two pages, one to list students and one to show details of a given student. The listing page has a link to the details page in each row of the students table, that opens a new tab in browser to show those details, when clicked.
Now the requirements changed to no more show details in a new tab, but in a modal dialog in the listing page.
My idea is to simply embed the details page content in the modal dialog so the listing page will not get too big and hard to maintain. Here start my doubts. After some research I changed the link in each row of the listing to the following button:
<p:commandButton value="Details" type="button"
onclick="PF('dialog-details').show()">
</p:commandButton>
The dialog is declared as follows:
<p:dialog widgetVar="dialog-details" header="Details" modal="true" width="95%">
<ui:include src="student_details.xhtml">
<ui:param name="id" value="#{student.id}"/>
</ui:include>
</p:dialog>
Finally, the details page was changed to be something like this:
<ui:composition
xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui" xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<f:metadata>
<f:viewParam name="id" value="#{studentBean.id}" />
</f:metadata>
<h1 class="title ui-widget-header ui-corner-all">Details of #{studentBean.bean.name} / #{studentBean.bean.number}</h1>
</ui:composition>
When I click the button, the dialog really shows and the content is the details page. I see the following content in the dialog:
Details of /
No errors at all, but the data that should be shown, isn't. A breakpoint was set in StudentBean.setId() (this method loads a property named bean with the Student instance corresponding to the passed id) but it is never hit.
After some time thinking about it, I came to understand why it does not work. The parameter passed to the details page is student.id, but student is the name used as the var in the <p:datatable/> that show all the students, so student is not valid in <p:dialog/> which is outside the <p:datatable/>.
So, what I need is a way to show the dialog using the id of the corresponding student in a given row. Ideally, I would like an ajax call here, so the details would loaded only when neded.
Any ideas?
The button should be an ajax button which sets the currently iterated entity in the bean, and then updates the dialog's content, and finally shows it. The dialog should just reference that entity in the bean and update the list and table on save. It's very important that dialog is placed outside the main form and that it has its own form.
Here's a kickoff example:
<h:form id="master">
<p:dataTable value="#{bean.entities}" var="entity">
<p:column>#{entity.property1}</p:column>
<p:column>#{entity.property2}</p:column>
<p:column>#{entity.property3}</p:column>
...
<p:column>
<p:commandButton value="View" action="#{bean.setEntity(entity)}"
update=":detail" oncomplete="PF('detail').show()" />
</p:column>
</p:dataTable>
</h:form>
<p:dialog id="detail" widgetVar="detail">
<h:form>
<p:inputText value="#{bean.entity.property1}" />
<p:inputText value="#{bean.entity.property2}" />
<p:inputText value="#{bean.entity.property3}" />
...
<p:button value="Close" onclick="PF('detail').hide(); return false" />
<p:commandButton value="Save" action="#{bean.save}"
update=":master" oncomplete="if(!args.validationFailed) PF('detail').hide()" />
</h:form>
</p:dialog>
With this inside a #ViewScoped bean:
private List<Entity> entities; // +getter
private Entity entity; // +getter+setter
#EJB
private EntityService entityService;
#PostConstruct
public void load() {
entities = entityService.list();
entity = null;
}
public void save() {
entityService.save(entity);
load();
}
See also:
Creating master-detail pages for entities, how to link them and which bean scope to choose
Creating master-detail table and dialog, how to reuse same dialog for create and edit
Keep p:dialog open when a validation error occurs after submit
Difference between rendered and visible attributes of <p:dialog>
How to display dialog only on complete of a successful form submit

PrimeFaces commandLink called only once in TabView

I'm using PrimeFaces 5.1. I have a TabView component in which the tabs are generated dynamically:
<p:tabView scrollable="true"
id="tabView"
dynamic="true" cache="false"
value="#{reportTabBean.tabs}"
var="tab"
activeIndex="#{reportTabBean.activeTab}">
...
</p:tabView>
The content of a tab is wrapped in a <p:outputPanel>. Inside the panel is a mix of plain HTML <table> tags and <ui:____> components. The tab contents make up a report. Each report has clickable elements created by <p:commandLink> that called a method in the manged bean. Here's is the simplified code:
<p:outputPanel id="bcx-scorecard-panel"
rendered="#{ tab['class'].simpleName eq 'CSAE2EScorecardShowTab'}">
<ui:repeat var="LOB"
value="#{tab.scorecardCSAE2E.getLOBs()}">
<table>
<tbody>
<ui:repeat var="metric"
value="#{tab.scorecardCSAE2E.getMetrics(LOB.filter)}">
<ui:fragment rendered="#{metric.type == 'metric'}">
<tr>
<ui:repeat var="dayNum"
value="#{tab.scorecardCSAE2E.daysInMonthIterator()}">
<td>
<ui:fragment
rendered="#{null != metric.getDataFor(dayNum).value and metric.showDogEar(dayNum)}">
<p:commandLink
immediate="true"
update=":dialogExceptionFillWriteOff, :exceptionFillWriteOffForm"
action="#{exceptionWriteBean.populateExceptionPopup(tab.title, LOB.filter, null, metric, tab.scorecardCSAE2E, dayNum)}"
oncomplete="PF('dlgExceptionFillWriteOff').show()">
#{metric.getDataFor(dayNum).value}
</p:commandLink>
</ui:fragment>
</td>
</ui:repeat>
</tr>
</ui:fragment>
</ui:repeat>
</tbody>
</table>
</ui:repeat>
</p:outputPanel>
Clicking the commandLink brings up a model dialog that displays details about that cell. Each cell represents data for a particular day. The problem is the dialog works only for the last tab. I can click each table cell and it'll display the correct data for that day. When I switch to a different tab and click on cells, the populateExceptionPopup method doesn't get called and instead the dialog shows the last pulled data from the other tab.
My beans are view scoped. I'm not sure if it has anything to do with it, but I'm using CDI beans and annotations. Here's one of the beans.
#Named("reportTabBean")
#ConversationScoped
public class ReportTabBean implements Serializable {
...
}

How to confirm an action using a modal window in JSF?

I have a JSF MyFaces dataTable that contains a list of elements and a column with a delete button. All I want to do is to popup a dialog when clicking on the delete button that would allow the user to confirm or cancel the operation.
I already have the dialog (reduced for simplicity and using <a> because of the lack of HTML5 support):
<div id="myModal">
<h:form>
<a data-dismiss="modal">Close</a>
<h:commandLink action="#{somethingMagicInHere?}">Confirm</h:commandLink>
</h:form>
</div>
In the dataTable I have something like this (also simplified):
<h:dataTable id="myDataTable" value="#{bean.elementList}" var="element">
<h:column>
<f:facet name="header">Actions</f:facet>
<a class="call-modal">Delete</a>
</h:column>
</h:dataTable>
Finally my ManagedBean looks like this:
#ManagedBean(name = "bean")
#RequestScoped
public class ElementClassBean {
...
public String actionToPerform(ElementClass e) {
MyBusinessLogicModel.getInstance().deleteElement(e);
}
}
So, in short, jQuery executes when loading the page and takes all elements with class call-modal and sets an onclick to them so that they display the component with id myModal, which is of course the modal window. I inherited this working this way and prefer not change it but any solution or ideas will help.
I can use a commandLink directly in the dataTable that would access actionToPerform(element) from the view but that, of course, won't fire the modal. So the main issue I see, given this structure, is how can I send the element being iterated in the dataTable to the modal once the Delete button is clicked? (I don't mind if the solution uses Ajax).
Any input will be helpful. Thanks.
Ok, this is the ugly but working solution that doesn't require me to refactor all the views and managed beans. In short: I added a hidden input field that would store the id of the element to delete in the modal form. In the datatable all I do is setting the value of the hidden input field once the button is clicked and fire the modal. The modal is then filled with the just updated value.
My simplified modal:
<div id="myModal">
<h:form id="myForm">
<h:inputHidden value="#{bean.elementIdInModal}" id="elementIdInModal"/>
<a data-dismiss="modal">Close</a>
<h:commandLink action="#{bean[actionToPerform]}">Confirm</h:commandLink>
</h:form>
</div>
My simplified dataTable:
<h:dataTable id="myDataTable" value="#{bean.elementList}" var="element">
<h:column>
<f:facet name="header">Actions</f:facet>
<h:link styleClass="call-modal"
onclick="$('#myForm\\:elementIdInModal').val(#{element.id})">
Delete
</h:link>
</h:column>
</h:dataTable>
My simplified ManagedBean:
#ManagedBean(name = "bean")
#RequestScoped
public class ElementClassBean {
private long elementIdInModal; // Ommiting getters and setters
public void actionToPerform() {
MyBusinessLogicModel.getInstance().deleteElement(elementIdInModal);
}
}

Can't rerender component which is bulilt through foreach

i have a div which consists of set of checkboxes built through foreach as follows:
<ice:panelGroup id="myDiv">
<c:forEach items="#{myBean.myCheckBoxes}" var="entry" varStatus="loop">
<input type="checkbox" name="myCheckBoxes" value="#{entry.value}" />
<span class="#{fn:contains(entry.value,'g') ? 'bold-style' : ''}">#{entry.key}</span>
</c:forEach>
</ice:panelGroup>
and i have an icefaces button in the same form of that div, and the button makes partial submit, i don't want to make full form submit.
<ice:commandButton value="Find" action="#{myBean.find}" partialSubmit="true">
<f:ajax execute="#this" render="myDiv" />
</ice:commandButton>
the search method:
public void find() {
// changes the map of the iteration
}
what happens, is that after executing search some components doesn't get removed from the div, although that they are not in the map, guess that the div is not getting refreshed/populated with data correctly, please advise, thanks.
solved by using ui:repeat instead of foreach.

Resources