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

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()"/>

Related

commandLink in primefaces datatable doesn't work if I add the filteredValue attribute

I have a primefaces datatable with a <p:commandLink> in each row. The user clicks it to see a different page with details about the record he selected. It was working fine until I add a filteredValue in my datatable. I need this attribute (filteredValue) in order to correctly filter and sort my datatable, as shown in this question.
But after adding this attribute, my commandLink stops working. How can I make it work with the attribute?
Here's my datatable:
<p:dataTable var="prot" value="#{myBean.listaProtocolos}" rows="15" filteredValue="#{myBean.listaProtocolosFiltrados}" sortBy="#{prot.dataEntradaArea}" sortFunction="#{myBean.sortXMLDatas}" sortOrder="descending" paginator="true" style="font-size: 0.9em;" paginatorPosition="bottom">
<p:column filterBy="${prot.nrProtocolo}" filterMatchMode="contains" width="8%" style="text-align:center">
<f:facet name="header">ID</f:facet>
<p:commandLink action="#{myBean.verDetalhesProtocolo}" process="#this messages" update="#this messages">
<h:outputText value="#{prot.nrProtocolo}" style="text-decoration: underline;"/>
<f:setPropertyActionListener target="#{myBean.nrProtocolo}" value="#{prot.nrProtocolo}" />
</p:commandLink>
</p:column>
(etc)
and the relevant pieces of myBean:
public void verDetalhesProtocolo() {
for(ProtocoloMY pro : this.listaProtocolos){
if(pro.getNrProtocolo().trim().equalsIgnoreCase(this.nrProtocolo.trim())) {
this.protocolo = new ProtocoloMY(pro);
break;
}
}
FacesContext facesContext = FacesContext.getCurrentInstance();
facesContext.getExternalContext().redirect("detalhes_protocolo_processo.xhtml");
//(This method isn't even called when I add the attribute filteredValue to my datatable)
public String getNrProtocolo() {
return nrProtocolo;
}
public void setNrProtocolo(String nrProtocolo) {
this.nrProtocolo = nrProtocolo;
}
public List<ProtocoloMY> getListaProtocolos() {
return listaProtocolos;
}
public List<ProtocoloMY> getListaProtocolosFiltrados() {
return listaProtocolosFiltrados;
}
public void setListaProtocolosFiltrados(List<ProtocoloMY> listaProtocolosFiltrados) {
this.listaProtocolosFiltrados = listaProtocolosFiltrados;
}
public void setListaProtocolos(List<ProtocoloMY> listaProtocolos) {
this.listaProtocolos = listaProtocolos;
}
And I almost forgot to say: There's some network traffic happening when I click the link, but nothing is shown in my backend console and the method in my bean isn't called.
I'm running primefaces v6.0.
For PrimeFaces to be able to track which row by its unique id you need to add the attribute rowKey="#{row.id}" to your p:datatable using whatever value in your row POJO that makes it unique.

Update primefaces datatable from actionListener [duplicate]

I am having difficulty re-rendering a PrimeFaces Datatable once a cell has been edited. Changing the value in one cell may change entries in the other cells, hence the need to refresh the entire table.
Here's the JSF page:
<h:form id="testForm">
<p:outputPanel id="testContainer">
<p:dataTable id="testTable" value="#{tableBean.data}" var="entry" editable="true" editMode="cell">
<p:ajax event="cellEdit" listener="#{tableBean.onCellEdit}" update=":testForm:testContainer" />
<p:column headerText="Col1">
<p:cellEditor>
<f:facet name="output"><h:outputText value="#{entry.col1}" /></f:facet>
<f:facet name="input"><p:inputText value="#{entry.col1}" /></f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Col2">
<p:cellEditor>
<f:facet name="output"><h:outputText value="#{entry.col2}" /></f:facet>
<f:facet name="input"><p:inputText value="#{entry.col2}" /></f:facet>
</p:cellEditor>
</p:column>
</p:dataTable>
<p:commandButton id="refreshButton" value="Redisplay" update="testContainer" />
</p:outputPanel>
</h:form>
And here's the backing bean:
#ManagedBean(name = "tableBean", eager = false)
#ViewScoped
public class TableBean {
public TableBean() {
RowData entry = new RowData("a1", "b1");
entries.add(entry);
entry = new RowData("a2", "b2");
entries.add(entry);
entry = new RowData("a3", "b3");
entries.add(entry);
}
public class RowData {
private String col1;
private String col2;
public RowData(String col1, String col2) {
this.col1 = col1;
this.col2 = col2;
}
public String getCol1() {
return col1;
}
public void setCol1(String col1) {
this.col1 = col1;
}
public String getCol2() {
return col2;
}
public void setCol2(String col2) {
this.col2 = col2;
}
}
private ArrayList<RowData> entries = new ArrayList<RowData>();
public List<RowData> getData() {
return entries;
}
public void onCellEdit(CellEditEvent event) {
entries.get(event.getRowIndex()).setCol1("Dummy Col 1");
entries.get(event.getRowIndex()).setCol2("Dummy Col 2");
}
}
When including update=":testForm:testContainer" within the cellEdit AJAX event, changing a cell value deletes the datatable on screen and only renders the cell content (along with the button) -- I do not understand why this is. When the update attribute is not specified, the table remains on screen with the active cell updated, but none of the other cells are updated (as to be expected).
The desired behaviour can be achieved (in a non-automated way) by not specifying the update attribute within the AJAX cellEdit event and clicking the Redisplay button after editing a cell's value. How can I achieve this in an automated way, and why does the update attribute not work as I expect?
I am using PrimeFaces 4.0.
The rowEdit and cellEdit events does by design inside the table not update/re-render anything else than the current row, even not when explicitly specified in update attribute. It's the consequence of PrimeFaces' a bit overzealous attempt to minimize the response size. This makes sense in most of the cases, but not in specifically your case. It's worth an issue report.
In the meanwhile, until they fix this behavior, your best bet is using <p:remoteCommand> to invoke the desired listener method and perform a full update of the table.
Rewrite
<p:dataTable ...>
<p:ajax event="cellEdit" listener="#{tableBean.onCellEdit}" update=":testForm:testContainer" />
...
</p:dataTable>
to
<p:remoteCommand name="onCellEdit" action="#{tableBean.onCellEdit}" update="testContainer" />
<p:dataTable ...>
<p:ajax event="cellEdit" oncomplete="onCellEdit()" />
...
</p:dataTable>
The BaLusC solution has not worked directly for me. The onCellEdit needs a CellEditEvent as param. My workaround is as following:
<p:remoteCommand name="onCellEdit" update="testContainer" />
<p:dataTable ...>
<p:ajax event="cellEdit" listener="#{tableBean.onCellEdit}" oncomplete="onCellEdit()" />
...
</p:dataTable>
If none of the solutions worked for you, this worked for me
<p:dataTable ... id="theId" widgetVar="theWidget" ...>
<p:ajax event="rowEdit" listener="#{...}"
oncomplete="PF('theWidget').filter()"/>
....
I'm calling the filter method on the PF widget on ajax complete, any method that does a "reload" of the table should work, I used filter because my table had column filters.
I tested your code. First I moved p:commandButton out of p:outputPanel. Here is modified code:
<h:form id="testForm">
<p:outputPanel id="testContainer">
<p:dataTable id="testTable" value="#{tableBean.data}" var="entry" editable="true" editMode="cell">
<p:ajax event="cellEdit" listener="#{tableBean.onCellEdit}" update=":testForm:testContainer" />
(...)
</p:dataTable>
</p:outputPanel>
<p:commandButton id="refreshButton" value="Redisplay" update="testContainer" />
</h:form>
I think this code doesn't work correctly. if you change anything in table, the p:ajax every time render full table. So, the program load basic data from TableBean constructor and deleted new data.
If I omit your p:ajax code there is not disapears any new data from screen. The refreshButton p:commandButton work correctly.
When including update=":testForm:testContainer" within the cellEdit
AJAX event, changing a cell value deletes the datatable on screen and
only renders the cell content (along with the button) -- I do not
understand why this is.
I think it is bad design add update=":testForm:testContainer" to ajax, because it's update your outputPanel more than as exepted (first time work correctly, second time couldn't edit cell, because the program update to many times table).
I don't know what is your goal. If you want render table without a commandButton, then could you specify one javascript event or p:message and this disappear you can render table.
I think if you omit update in p:ajax or specify update of one p:message, and move p.commandButton out of testContainer your code start work correctly.
After 5 years, this problem still exists. Unfortunately, while Baukes solution is extremly helpful and includes important insights it's still incomplete, as ltlBeBoy already pointed out in his comment. Subsequent edits without change lead to an inconsistent table state, where no more edits are possible. The reason is, that the oncomplete remote update comes after the edit mode of the new cell is already activated. So the edit mode of the new cell is destroyed by the update. However, the update can't be done in Ajax listener tableBean#onCellEdit, as this would display the table erroneously with one cell only.
The solution is, to execute the update in the remote commands listener and only, if a change happend. So, in tableBean you implement a programmatic update, a remote listener and a flag that indicates change:
public static void update(String id) {
PrimeFaces pf = PrimeFaces.current(); //RequestContext.getCurrentInstance() for <PF 6.2
if(pf.isAjaxRequest()) pf.ajax().update(id);
}
/** Whether onCellEdit changed the value */
boolean onCellEditChange;
public void onCellEditRemote() {
if(!onCellEditChange) update("testContainer");
}
public void onCellEdit(CellEditEvent event) {
... onCellEditChange= /*Change happend*/ ...
}
The remote command has no update attribute any more:
<p:remoteCommand name="onCellEdit" actionListener="#{tabelBean.onCellEditRemote}"/>
try using process
<p:remoteCommand name="onCellEdit" update="testContainer" process="#this" />
<p:dataTable ...>
<p:ajax event="cellEdit" listener="#{tableBean.onCellEdit}" oncomplete="onCellEdit()" process="#this" />
...
</p:dataTable>
This is my first post ever in here. As ltlBeBoy mentioned, BalusC's solution works only if the cell editing is done with an enter key hit.
None of the other suggestions listed in here worked for me.
In my case, I just wanted to update a specific row (that has each column's average) in the table on cellEdit event. I'm posting this in case if someone out there is looking for a solution: I managed to achieve this by separating that row in a second datatable right below the main one. Here's the code:
<pf:ajax event="cellEdit"
listener="#{newAgentMetricBacking.onCellEdit}"
update="c21413" />
<pf:dataTable id="c21413"
styleClass="noHeader"
value="">
<pf:column>
<h:outputText value="#{bundle.TeamAverage}"/>
</pf:column>
<pf:columns columnIndexVar="j"
value="#{newAgentMetricBacking.averageArray}"
var="avg">
<h:outputText value="#{newAgentMetricBacking.
averageArray[j]}"/>
</pf:columns>
</pf:dataTable>
Otherwise, as of today, I couldn't find a better solution for updating either the whole table or a specific row specifically upon success of cellEdit event.
Cheers!

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.

Contextmenu and target with dataTable

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.

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>

Resources