Contextmenu and target with dataTable - jsf

I'm doing jsf with primefaces5.0 and here's my scenario:
I have a page contains a dataTable with contextmenu like this:
When I clicked the contextmenu, I want to popup another "window" (not "tab") with information about the selected data, for example, the String "data1".
However, it can't be done whether I use action or url parameter in the p:menuitem.
When I use action parameter, anotherPage shows but in the original window, while the opened new window is empty:
And if I change action="/anotherPage" into url="/anotherPage.xhtml",anotherPage shows in the new window but with no information about the selected data:(Please note that the title has changed into "Another Page")
Here's what I've done:
Facelet:
<h:form>
<p:contextMenu for="dataTable">
<p:menuitem value="clickMe" icon="ui-icon-gear"
onclick="window.open('', 'Popup', config = 'scrollbars=yes,status=no,toolbar=no,location=no,menubar=no,width=1300,height=370').focus();"
target="Popup" actionListener="#{mainBean.showPopup()}" action="/anotherPage"/>
</p:contextMenu>
<p:dataTable id="dataTable" value="#{mainBean.dataList}" var="data" rowIndexVar="index" emptyMessage="Loading..."
selectionMode="single" selection="#{mainBean.selectedStr}" rowKey="#{data}">
<p:column headerText="data">
<p:outputLabel value="#{data}"/>
</p:column>
</p:dataTable>
</h:form>
BackingBean:
private List<String> dataList=new ArrayList<>();
private String selectedStr="";
public void showPopup(){
Map<String, Object> reqMap=FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
reqMap.put("param", selectedStr);
}
AnotherPage.xhtml:
<p:outputLabel value="This is the selected String:"/>
<p:outputLabel value="#{anotherPageBean.txt}"/>
AnotherPageBean:
private String txt;
#PostConstruct
public void init(){
Map<String, Object> reqMap=FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
txt=(String) reqMap.get("param");
}
Thank you very much.
Update
I did a lot survey and find out something:
Target attribute of menuitem is not rendered for action, it's a known issue and won't be fixed.
Menuitem supports f:params to pass parameters according to optimus.prime.
When I use url, the parameters can't be find in request map, which probably means it's a brand new request so that I can't find what I put in the reqMap.
Maybe I can put parameters in session map instead of request map because of what I mentioned in 3.
Neither action nor actionListener be called if I use url.

According to what I've said in the update section of my question, I found a workaround.
Please note that I believe this is just a workaround, hope there are some normal ways to solve it.
As target attribute of menuitem is not rendered for action I can only use url.
So I tried using f:params to pass parameters with url but still in vain because the 3rd point I mentioned in the update section I think.
After that, I tried put parameters in session map through backing bean and use action to call the method. However, it still won't work because action is not called if I use url.
According to this thought, I call the method outside the menuitem.
I add a event to the dataTable and call method there:
<p:dataTable id="dataTable" value="#{mainBean.dataList}" var="data" rowIndexVar="index" emptyMessage="Loading..."
selectionMode="single" selection="#{mainBean.selectedStr}" rowKey="#{data}">
<p:ajax event="contextMenu" listener="#{mainBean.setParam()}"/>
<p:column headerText="data">
<p:outputLabel value="#{data}"/>
</p:column>
</p:dataTable>
And in the Backing Bean:
public void setParam(){
HttpSession session=(HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
session.setAttribute("param", selectedStr);
}
So I can get info through session in anotherPageBean like:
#PostConstruct
public void init(){
HttpSession session=(HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
txt=(String) session.getAttribute("param");
session.removeAttribute("param");
}
Please note that the attribute should be removed after the info is got.
And this is the result:
Just for those who have the same problem.

Related

JSF page content not being updated

I have a JSF application in wich i'm using ui:composition/ui:include to display some elements inside a page like in the following code
template.xhtml
...
<h:body>
...
<h:panelGroup id="mainPanel">
<ui:include src="#{myBean.page}"/>
</h:panelGroup>
...
</h:body>
items-list.xhtml
<ui:composition>
...
<h:form>
<p:dataTable var="item" value="#{myBean.items}">
<p:column>
<h:commandLink action="#{myBean.loadItemDetail(item)}"
process="#this"
update="mainPanel">
<h:outputText value="#{item.name}"/>
</h:commandLink>
</p:column>
</p:dataTable>
</h:form>
...
</ui:composition>
items-detail.xhtml
<ui:composition>
<h:form>
<!-- Some code to display and modify the item details-->
</h:form>
<ui:composition>
MyBean.java
#ManagedBean
#ViewScoped
public class MyBean {
private String page;
private List<Item> items;
private ItemDetail itmDetail;
#PostConstruct
private void init() {
page = "items-list.xhtml"
items = ... //some logic to populate the items list
}
public void loadItemDetail(Item item){
itmDetail = ... //some logic to get the item's detail
page = "item-detail.xhtml"
}
}
The first time I click on an item to see the details it works fine but after that, if I click on the browser's back button and try to load a different item details it keeps showing me the details of the first item.
I check the JSF lifeCycle for each call and although in every call the application goes through all the satages, only the first one calls the bean method locadItemDetail. is there a reason why my method is being ignored after the first success call? is there some kind of cache on JSF where my data is bean taken from instead of my Bean?
Also i tried to avoid this behavior implementing a filter as is suggest in this post and it kind of works but now when I click browser's back button it show me an annoying page preventing me from resend the form; is there a way to prevent that?
Note:
I know that i could change the app to use GET method instead of POST to avoid this cache problem but it will required some serious changes over the app so that's not an option.
Any help or guidance about why could this be happening will be appreciated.

Primefaces datatable update in f:facet name="header" not working

I want to update a button in the header facet of a primefaces datatable and it does not work. I copied the button outside the datatable everything works fine.
The update should happen when the filter event of the datatable gets fired. I explicitly update the datatable, the outside- and the inside-button.
The intention is to display a button with an icon when no filter is set and another icon when a filter is used. In this example I simplyfied the use case: when no filter is used there is a open-lock icon, if I type something in a filter a closed-lock icon should be displayed. To release the lock one has to click the button (I did not implement the deletion of the filter in the datatable).
From what I understand I use the correct ID of the button inside the header. So I do not know why this does not work?
I am using mojarra 2.2 and primefaces 6.
<h:form id="id_form">
<p:dataTable
id="id_table"
value="#{stateController.names}"
var="currentName">
<p:ajax
event="filter"
listener="#{stateController.markLocked()}"
update="id_table id_form:id_table:id_button_inside id_form:id_button_outside"/>
<p:column
filterBy="#{currentName}"
filterMatchMode="contains">
<f:facet name="header">
<p:commandButton
id="id_button_inside"
action="#{stateController.markUnlocked()}"
icon="#{stateController.locked ? 'ui-icon-locked' : 'ui-icon-unlocked'}"
update="id_form"/>
</f:facet>
<h:outputText value="#{currentName}" />
</p:column>
</p:dataTable>
<p:commandButton
id="id_button_outside"
action="#{stateController.markUnlocked()}"
icon="#{stateController.locked ? 'ui-icon-locked' : 'ui-icon-unlocked'}"
update="id_form"
/>
</h:form>
#Named(value = "stateController")
#SessionScoped
public class StateController implements Serializable
{
private boolean locked;
private List<String> names;
#PostConstruct
private void init()
{
locked = false;
names = new ArrayList<>();
names.add("peter");
}
public void markLocked()
{
locked = true;
}
public void markUnlocked()
{
locked = false;
}
// getter + setter omitted
}
I also tried to put a button in a separate column. With this button (which is displayed in every row of the datatable) everything works fine as well.
It's a bit late, but maybe someone will find it useful some day.
To solve Filou's problem, you need to define remoteCommand outside dataTable and make it update dataTable's header facet.
<p:remoteCommand name="rmtCommand" update="id_form:id_table:id_button_inside"/>
<p:dataTable
id="id_table"
value="#{stateController.names}"
var="currentName">
<p:ajax
event="filter"
listener="#{stateController.markLocked()}"
oncomplete="rmtCommand()"/>

Primefaces' commandLink works only on the first page of a dataTable

I have a column with p:commandLink inside p:dataTable, which has a paginator. When user clicks on a commandLink, a dialog opens, displaying data of that row.
datatable html code:
<p:dataTable id="dtSample" value="#{sessionBean.sampleList}"
binding="#{requestBean.dtSampleList}"
paginator="true" paginatorPosition="bottom"
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="#{10,25,50}"
currentPageReportTemplate="({startRecord} of {totalRecords})"
var="item" emptyMessage="No entries." rows="10" rowKey="#{item.id}">
<p:column headerText="Id">
<p:commandLink id="lnkSample"
action="#{requestBean.onLinkClick(item)}"
oncomplete="PF('dlgSample').show();"
update="#(.dialogClass)">
<h:outputText value="#{item.id}" />
</p:commandLink>
</p:column>
<p:column headerText="Code">
<h:outputText value="#{item.code}" />
</p:column>
<p:column headerText="Description">
<h:outputText value="#{item.descr}" />
</p:column>
</p:dataTable>
Request bean:
public class RequestBean {
private SessionBean sessionBean;
private DataTable dtSampleList;
public void init() {
// load samle list
loadSampleList();
}
public String onLinkClick(Sample sample) {
getSessionBean().setSelectedSample(sample);
return "success";
}
private void loadSampleList() {
List<Sample> list = new ArrayList<Sample>();
for (int i = 0; i < 100; i++) {
Sample tmp = new Sample();
tmp.setId(new BigDecimal(i + 1));
tmp.setCode("code" + (i + 1));
tmp.setDescr("desc" + (i + 1));
list.add(tmp);
}
getSessionBean().setSampleList(list);
}
// getters and setters
}
Session bean:
public class SessionBean implements Serializable {
private static final long serialVersionUID = 1L;
private List<Sample> sampleList;
private Sample selectedSample;
// getters and setters
}
dialog html code:
<p:dialog id="dlgSample" closeOnEscape="true" widgetVar="dlgSample"
styleClass="dialogClass" modal="true">
<p:panelGrid columns="1">
<h:outputText value="Id: #{sessionBean.selectedSample.id}" />
<h:outputText value="Code: #{sessionBean.selectedSample.code}" />
<h:outputText value="Description: #{sessionBean.selectedSample.descr}" />
</p:panelGrid>
</p:dialog>
When I click on a link on the first page of a datatable, link action is executed and a dialog, displaying row data, is properly refreshed. But when I move to any of the following pages of a datatable, clicking a link doesn't refresh the data in a dialog (the link action is not called, so the data in a dialog is wrong - selectedSample variable has old value). Of course when I go back to the first page of the datatable, command link works again (action method is called and data is refreshed).
What am I doing wrong? Why is action method not called on any datatable page?
I'm using Primefaces 5.2.
Looks like the problem was in a PF dataTable component. There were first and rows attributes missing and after adding them, commandLinks on all pages work as expected.
What am I doing wrong? Why is action method not called on any datatable page?
A common misstake leading to that behaviour is that your state is not consistent: To process the form submit, JSF will recreate the exact same view as in the prior request, and then apply the changes (execute the action)
If this new view is now different than the view before the submit was, every action is aborted and not invoked. (Different in terms of involved elements. I.e. if your commandLink was rendered="true" before submitting, it needs to be rendered="true" during the APPLY_REQUEST_VALUES-Phase).
So, from your description I would assume, that your table is falling back to page 1, which will remove any link on page 2 from the view and abort it's view action, because the element is no longer rendered.
For the same reason, links on page 1 are working, because even if your table looses track of the current page - it will render page 1, so you have the same view as before, so the submission works.
You could easily verify this, by increasing the number of elements per page and see that every link moved from page 2 to page 1 is now working.
But without seeing the whole bean I can only assume this. For easy testing, set your bean to #SessionScoped and see if this resolves it.
For more ideas, see this post: commandButton/commandLink/ajax action/listener method not invoked or input value not updated
When putting components such as p:commandLink in a p:dataTable row, you should always specify
process="#this"
So, in your case, I would try :
<p:commandLink id="lnkSample"
action="#{bean.onLinkClick(item)}"
oncomplete="PF('dlgSample').show();"
update="#(.dialogClass)">
<h:outputText value="#{item.id}" />
</p:commandLink>
Doing so, you are sure that only the click on the link will be processed, and no other submission will be performed.

Add a new row to a p:datatable then submit content

I'm trying to add a new row in a Primefaces datatable, then I would like to submit the content of this table and do some business logic. The datatable model is a collection that is maintained in a ViewScoped managed bean.
I'm using JSF 2.1 with Primefaces 3.3.
Short example:
<h:form id="my-form">
<p:dataTable value="#{testBean.list}" var="s" id="datatable">
<p:column>
<h:inputText value="#{s}"/>
</p:column>
<f:facet name="footer">
<p:commandButton value="Add row" action="#{testBean.addRow()}" process="#form" update="#form" immediate="true" />
<p:commandButton value="Do stuff" action="#{testBean.doSomeLogic()}" process="#form" update="#form"/>
</f:facet>
</p:dataTable>
</h:form>
Managed Bean:
#ManagedBean
#ViewScoped
public class TestBean implements Serializable {
private List<String> list;
public TestBean() {
}
#PostConstruct
public void init() {
list = new ArrayList<String>();
list.add("one");
list.add("two");
}
public void addRow(){
list.add(new String());
}
public void doSomeLogic(){
for (String string : list) {
System.out.println(string);
}
}
// getters and setters
}
What actually happens:
the user clicks on "add row" button, a new row is added (I need immediate to be true so no validation is done, those fields are part of a bigger form).
the user clicks on "do stuff", the collection has the right size (with new rows) but the user's input in not taken into account (neither modification to pre exiting rows, nor new values in freshly added rows).
What can I do to submit the new values too? I'm only beginning JSF and I'm not sure I'm already 100% getting it.
Thanks for your help.
Possible duplicates:
Add a row to h:dataTable via AJAX with request-scoped bean without losing the row data
How to Dynamically add a row in a table in JSF?
JSF datatable: adding and removing rows clear rows values
Edit: problem is solved thanks to Jitesh, a working example can be found here: JSF2, can I add JSF components dynamically?
The only problem is you are using immutable object in inputText. To understatnd this check out BaluC's Answer
According to it "As being an immutable object, the String doesn't have a setter method. The will never be able to set the entered value."
Try to remove immediate attribute from the commandButton you will find that on insertion of each row the data will be cleared.
If I understand correctly, there are some validations elsewhere in the form that are failing. When any of the submitted form values fail validation then none of the submitted values are applied to the managed bean unless immediate is used. This is why it seems that you are able to add a new row but not with the doStuff method. You did not add immediate to doStuff.
But stating that there are a few things you could do much more cleanly and efficiently.
First, the action attribute should really be used for navigation actions. JSF expects that methods bound to an action have a return value that represents the navigation result. For void methods it is better to use actionListener. For more information on the difference between action and actionListener read here: Differences between action and actionListener
Secondly, why not just set process and update to only the data table component and then you don't have to worry about other form validations? Here is an example:
<h:form id="my-form">
<p:dataTable value="#{testBean.list}" var="s" id="datatable">
<p:column>
<h:inputText value="#{s}"/>
</p:column>
<f:facet name="footer">
<p:commandButton value="Add row" actionListener="#{testBean.addRow}"
process=":my-form:datatable" update=":my-form:datatable" />
<p:commandButton value="Do stuff" actionListener="#{testBean.doSomeLogic}"
process=":my-form:datatable" update=":my-form:datatable" />
</f:facet>
</p:dataTable>
</h:form>

Firefox retain the content of an array inside JSF-Viewscoped-Managed-Bean even after refresh the page

This odd behavior only happen in Firefox (specifically Firefox 8). So I have a dataTable that I can do multiple selection. A submit button, that will display a list of selected items to a dataList and to a dialog. If the user did not select anything, then a error msg come up asking the user to select something. The dialog will not appear if the user select nothing. The below code does all that. However FireFox behaves oddly if you do these follow:
Click to select an item on the dataTable
Then refresh (F5 or Ctl + R) the page (you can see the selection got clear off)
Then click submit, it show whatever I just selected.
This is unexpecting, since the refresh should clear out whatever you just select due to nature of #ViewScoped bean. This behavior only happen in Firefox. IE 8 behave correctly for me. Is this a bug, or am I doing something wrong here?
Mojarra 2.1 + PrimeFaces3.0 Final + Tomcat 7
UPDATE: I did some debugging, when I refresh page, the value of the array selectedFoods become null, but for some odd reason, when it get to public void checkSelection(), it hold the value of the previous selection. So odd.
Here is my code.
<p:growl id="messages" showDetail="true" />
<p:messages id="msgs"/>
<h:form id="form">
<p:dataTable value="#{viewBean.foodList}" var="item"
selection="#{viewBean.selectedFoods}"
selectionMode="multiple"
rowKey="#{item}">
<p:column>
#{item}
</p:column>
<f:facet name="footer">
<p:commandButton value="Submit" update=":form:display :dataList"
action="#{viewBean.checkSelection}"/>
</f:facet>
</p:dataTable>
<p:dataList id="display" value="#{viewBean.selectedFoods}" var="item"
itemType="disc">
#{item}
</p:dataList>
</h:form>
<p:dialog id="dialog1" widgetVar="dialog1" dynamic="true" width="200">
<p:dataList id="dataList" value="#{viewBean.selectedFoods}" var="item"
itemType="disc">
#{item}
</p:dataList>
</p:dialog>
Here is my managed bean
#ManagedBean
#ViewScoped
public class ViewBean implements Serializable {
private List<String> foodList;
private String[] selectedFoods;
#PostConstruct
public void init() {
foodList = new ArrayList<String>();
foodList.add("Pizza");
foodList.add("Pasta");
foodList.add("Hamburger");
}
public void checkSelection(){
RequestContext requestContext = RequestContext.getCurrentInstance();
if(selectedFoods.length > 0){
requestContext.execute("dialog1.show()");
}else{
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Error", "Please select"));
requestContext.addPartialUpdateTarget("messages");
}
}
//setter, getter
}
Your code is fine. What you're seeing is because of something that is supposed to be a feature of Firefox (I was able to reproduce this on FF4). The selection model for p:dataTable is implemented with a hidden form field. When reloading a page, Firefox tries to save and restore form field values that have changed so that you don't lose what you entered. You can observe this by adding a <h:inputText/> to your view, typing something in the input, and reloading.
I'm not sure that the Firefox team meant for this to apply to hidden form fields, but I figure there's a decent chance that they did. I plan to file a bug report with Primefaces to either initialize the hidden input or to read the input on load to make the p:dataTable selection match. Either solution should result in the rendered selection and the hidden selection model to be in sync.

Resources