Too much data in p:dataTable results in broken row/cell edit submits - jsf

I've been using datatables to allow users to enter and edit large amounts of data, but have found that once a large (but uncertain) amount of columns and rows are reached, the form becomes unable to be submitted, and all forms of communication with the server are cut off (unrelated buttons, ajax, etc) until a page refresh is performed.
Is this a limitation of my computer/browser, or is there a hard-coded limit on the maximum quantity of editable data able to be displayed in a datatable? Could this be because of a maximum limit to the number of input fields in a form?
Is the only solution to use paginator for my datatables?
The following is an example that will fail (for me) to be editable if there are too many rows/columns.
<h:form>
<p:dataTable var="_var" value="#{bean.values}" editable="true">
<p:column headerText="Value">
<p:cellEditor>
<f:facet name="output"><h:outputText value="#{_var.value}" /></f:facet>
<f:facet name="input"><p:inputText value="#{_var.value}" /></f:facet>
</p:cellEditor>
</p:column>
<!-- Copy and paste this^ column 15 times -->
<p:column>
<p:rowEditor />
</p:column>
</p:dataTable>
</h:form>

When using row or cell editor, instruct the cellEdit event to use partialSubmit="true".
<p:dataTable ... editable="true">
<p:ajax event="cellEdit" process="#this" partialSubmit="true" />
...
</p:dataTable>
This will send only the input fields covered by process attribute (which is either the row or the cell itself) instead of every single input field.
See also:
How to decrease request payload of p:ajax during e.g. p:dataTable pagination

Okay, I found a solution.
Apparently each input is sent as a GET/POST parameter, and there is a default limit of 512 on servers to prevent DoS attacks.
Unfortunately, Primefaces does not seem to display this error, making it very hard to find.
EDIT: It displays the error, if the logging is set to the correct level. The error is also displayed in the F12 developer tools debugging log.
In short, you must go to your servers standalone.xml and add:
<property name="org.apache.tomcat.util.http.Parameters.MAX_COUNT" value="10000"/>
Under <system-properties>
EDIT
I refused to give up on a better solution than sending thousands of parameters, and revisited this problem.
It seems that PrimeFaces 5.2 added in the <p:ajax partialSubmitFilter=""> attribute, which allows filtering the inputs that you want. This didn't work for me, as I couldn't narrow down the submitted data to the row using just this.
Instead, I decided to override the PrimeFaces.ajax.Request.send function, like so:
var pfsend = PrimeFaces.ajax.Request.send;
PrimeFaces.ajax.Request.send = function(cfg) {
if (cfg.event === 'rowEdit' || cfg.event === 'rowEditCancel') {
// This is an operation on a datatable row. Filter for only that row
cfg.partialSubmitFilter = '[data-ri=' + cfg.ext.params[0].value + '] :input';
}
pfsend(cfg); // Run the original function
};
In this way, the value of the first parameter of the cfg (which is the row being edited) is used as the value for finding an element with the attribute data-ri set to that value (the row).
Note that this does require you to have primefaces 5.2, but for older versions, the same can be achieved by overriding the entire package, and tactically placing similar lines in the right place of the same function (mind that cfg.partialSubmitFilter will not exist).

I found more elegant solution, without overriding function PrimeFaces.ajax.Request.send and still only have it happen on the rowEdit event.
Firstly, define javascript function:
function sendOnlyEditedRow(cfg) {
cfg.partialSubmitFilter = "[data-ri=" + cfg.ext.params[0].value + "] :input";
}
Then call it in onstart callback:
<p:ajax onstart="sendOnlyEditedRow(cfg)" event="rowEdit" listener="#{cc.attrs.detectedFormations.confirmDetectedFormation(i)}"
process="#this detectedFormations" update="detectedFormations"
partialSubmit="true" />

Related

use of cellEditor in facet not working primefaces

In a project, I have to merge two datatables (the first to set the header of the second and the second displaying the informations contained in an uploaded file) into one to simplify the legibility on the page.
Since the first datatable was using a cellEditor I tried using one in the merged one the following way for the header:
<f:facet name="header">
<p:cellEditor>
<f:facet name="output">
<h:outputLabel id="output" value="some text" />
</f:facet>
<f:facet name="input">
<p:selectOneMenu value="#{handler.selectItems}">
<!-- List of selectItems -->
<p:ajax event="change" update="output" />
</p:selectOneMenu>
</f:facet>
</p:cellEditor>
</f:facet>
But when I build the project and click on the header of my columns, I don't have any list of selectItems appearing to give me the possibility to change the header. It was used in <p:column> before without problem so I imagine the <f:facet> is the problem
I succesfully made the possibility to change the value of the header using <p:inplace> instead, but it doesn't behave exactly like I want.
I want that even if I don't modify the value when I click on the header (and make the list appears), I can click somewhere on the page and make the list disappear to have a "clean" header. I don't really want to use the editor="true" option of inplace because the goal is also to try to reduce the number of click needed for the user. I also made it a bit using <p:ajax event="blur" update="output" /> but some value of the list of selectItems make one or two inputText appear. And with that event, when I click in the text box, it closes the field automatically and I can only the value of the field once. After, the moment I click on the list to open it, it closes the field.
How can I fix that ?

selection issue p:dataTable with selectionmode set to multiple nested in p:dataGrid

I'm working on a PrimeFaces project, where I choose to use a p:DataTable inside a p:DataGrid. The DataTable has the selectionMode attribute set to "multiple". I have a button on the page and when I click on it, I want to get all the selected checkboxes inside the various generated tables. All the checkboxes bring the same type of information (they are in different tables because of an information grouping scheme I use in the project).
Since I have multiple DataTables in the final rendered page, how can I get the selected checkboxes? I tried using the selection="#{myBean.selectedOptions}" attribute of the DataTable, but it seems to get just one of the generated tables' selected options (aparently the first one). What can I do to get the selected options of all tables generated by the p:DataGrid? Javascript? Is there any point I'm missing in the DataTable behavior?
(Well.. I don`t know whether I was able to make myself clear enough - English is not my native language).
[edited]
More information:
The number o DataTables generated is variable
I tried using the jsf visit method to traverse the user interface component view as shown here, looking for all checkboxes inside the DataGrid, but it couldn't find any of the checkboxes (I printed out to the console all the elements found). I think it's because the checkboxes are encapsulated in DataTable component. This made me think about not using the selectionMode="multiple" and, instead, manually add the checkboxes and a hidden input element for each one so I can find the checkboxes (or the "input hidden") using the visit method. The sad part of this aproach is that I'd have to stop using some good stuff of the DataTable selectionMode="multiple" feature, like hilighting the row if the corresponding checkbox is checked, and the ready to use "check all" checkbox it automatically put in the header of the ckeckboxes column.
I tried using a nested List in the selection attribute, but it didn't work. The main idea was using something like selection="#{bean.myListOfLists.get(t.counter)}". The "counter" variable is taken from the very object that is used to fulfill the current row of the DataTable (represented by the t variable in the attribute var="t"). The DataTable seems to not accept this approach. It doesn`t fulfill the nested lists.
I tried using the above approch with a modification: instead of a list of lists, a build a new class "B" and made a list of B (selection="#{bean.myListOf_B_Objects.get(t.counter).listInsideBObject}"). The result is simliar to the last approach: none of the lists inside the B objects were fulfilled.
Below there is a simplified view of what I did (more similar to the last case, but it's just to get the big picture)
<p:dataGrid var="gridCellContent"
value="#{myBean.dataGridContent}"
layout="grid" id="id01" columns="3"">
<p:dataTable var="something"
value="#{gridCellContent.listOfSomething}"
rowKey="#{something.id}"
selection="#{myBean.listOf_B_Objects.get(gridCellContent.counter).listInside_B_Object}">
<p:column selectionMode="multiple" />
<p:column>
<h:outputText value="#{something.sometext}" />
</p:column>
</p:dataTable>
<f:facet name="footer">
<p:commandButton id="btnSave" value="Save"
actionListener="#{myBean.btnSave}"/>
</f:facet>
</p:dataGrid>
[edited 2] Following #JaqenH'ghar's sugestion I tried out the modifications below, which unfortunately still din't work:
<p:dataGrid var="gridCellContent"
value="#{myBean.dataGridContent}"
layout="grid" id="id01" columns="3"
rowIndexVar="count">
<p:dataTable var="something"
value="#{gridCellContent.listOfSomething}"
rowKey="#{something.id}"
selection="#{myBean.listOfLists.get(count)}">
<p:column selectionMode="multiple" />
<p:column>
<h:outputText value="#{something.sometext}" />
</p:column>
</p:dataTable>
<f:facet name="footer">
<p:commandButton id="btnSave" value="Save"
actionListener="#{myBean.btnSave}"/>
</f:facet>
</p:dataGrid>
And for myBean:
#ManagedBean
public class MyBean {
...
private List<List<Something>> listOfLists = new ArrayList<List<Something>>();
public List<List<Something>> getListOfLists(){
this.listOfLists.add(new ArrayList<Something>());
return this.listOfLists;
}
public void setListOfLists(List<Something> listOfSomething) {
this.listOfLists.add(listOfSomething);
}
...
}

p:dataList inside of p:dataTable

I'm using <p:dataTable> to show the values of my files. Inside of each file is a list of metadates. I want to show each metadate as an own column beside the other columns of the file. (I excluded the other columns to make the code more readable).
So, my problem is, that with the <p:dataList> it does not show the columns. The name of the meta is the same in every metadata, if someone believes this to be the problem.
I already tried to use <p:dataTable> instead of <p:dataList>, but as my customer originally wanted to have the metadata shown as own column this would not be my preferred way, but on the other hand it is working.
<p:dataTable id="fileList" var="f" value="#{reader.files}" rendered="#{reader.showFileTable()}">
<p:dataList value="#{f.meta}" var="meta1">
<p:column>
#{meta1.value}
</p:column>
</p:dataList>
<p:column headerText="Metadaten">
<p:dataTable value="#{f.meta}" var="meta">
<p:column headerText="#{meta.name}">
#{meta.value}
</p:column>
</p:dataTable>
</p:column>
</p:dataTable>
I am really grateful for any help, how I could make the code work so that each metadate has it's own column in the fileList-dataTable.
P.S: I already tried c:forEach and ui:repeat which also didn't work.
P.P.S: I forgot to mention, that my customer sometimes don't want to see specific metadates, so it would be very nice when it is possible to do this with the suggested solution.
You can build columns dynamically in primefaces by using the <p:columns> tag see the vdl
check the primefaces showcase for an example
Suppose you have this code:
<p:dataList id="fileList" var="f" value="#{reader.files}">
<p:dataTable value="#{f.listItems}" var="meta1">
<p:column>#{meta1.value}</p:column>
<p:columns value="#{meta1.listItems}">...</p:columns>
</p:dataTable>
</p:dataList>
Dynamic columns in nested dataTable are not generated: it seems p:columns cannot refer var like "f" and "meta1".
I can understand "meta1" is not visible, because it's producing header's table, but I really can't understand why "f" is not visible at that time.
Any idea? Thanks.

How to clear global filter in PrimeFaces DataTable

How can I clear global filter in PrimeFaces DataTable using "Clear" CommandButton? I found similar question on PrimeFaces forum, but looks like not answered - the tip one can find there didn't help me. I tried to solve the problem as follows:
<p:dataTable id="myTab" widgetVar="myTabWidgetVar" var="obj"
value="#{managedBean.objects}">
<f:facet name="header">
<p:outputPanel>
<h:outputText value="Search all fields:"/>
<p:inputText id="globalFilter" onkeyup="myTabWidgetVar.filter()"/>
</p:outputPanel>
</f:facet>
</p:dataTable>
<p:commandButton value="Clear" onclick="myTabWidgetVar.clearFilters();"
update="#form"/>
It clears column filters only. Value in global filter remains uncleared. Can you help me to solve the problem?
Here is how:
$("#someFormId\\:myTab\\:globalFilter").val("").keyup();//if you got `prependId="false" , than omit the `someFormId\\:` part from the selector and leave only `myTab\\:globalFilter`
This will fill your filter with empty string and trigger a keyup event , resulting in clearing your filter and clearing the filter state of the table
If you just want to clear the filter without resetting the filter applied to your table use
$("#someFormId\\:myTab\\:globalFilter").val("");
If you want to use this code in a commandButton there is no need to use ajax (update...)
Just use onclick like this
<p:commandButton value="Clear"
onclick="$('#someFormId\\:myTab\\:globalFilter').val('').keyup(); return false;"/>
use return false; so the button wont submit your page...
<pdataTable widgetVar="departments" />
<p:commandButton value="#{lang:text('Add department')}"
id="....."
actionListener="....."
update="#form"
oncomplete="PF('departments').clearFilters()"
styleClass="........."/>
It works in my case for prime faces
Primefaces datatable has "filteredValue" option. Let's assume you set filtered values like; filteredValue="#managedBean.filteredObjects}".
filteredObjects has the same type with datatable's value which is "managedBean.objects" in your case. Before you load datatable, set filteredObjects variable to NULL. It will solve your problem.

input binding in ui:repeat in jsf

i am using facelets jsf 2.0 with primefaces 3.0.M2 component library.
i am trying to achieve dynamic numbers of rows including iput fields that are filled when a datatable selection occurs.
whenever a selection is made the dynamic rows generated correctly with input fields but after the first selection for following selections dynamic row count changes correctly but the input fields does not update and keeps showing inputs from the first selection.
here is how i iterate list in facelet;
<ui:repeat value="#{goalEntranceBean.selectedCard.parameterList}" var="prmBean" >
<li><h:outputText value="#{prmBean.lookUp.value}"/></li>
<li>
<h:outputText value="Weight:"/>
<p:inputText id="wx" required="true" value="#{prmBean.weight}">
</p:inputText>
<h:outputText value="Percent:"/>
<p:inputText required="true" value="#{prmBean.percent}">
</p:inputText>
</li>
</ui:repeat>
my bean where i get the list of cards and set the selectedCard with rowSelect event in datatable.
#ManagedBean(name = "goalEntranceBean")
#ViewScoped
public class GoalEntranceAction implements Serializable {
private List<ScoreCard> personalCards = new ArrayList<ScoreCard>();
private ScoreCard selectedCard = new ScoreCard();
......
}
when i checked in debug mode i can see the true list but in screen the elements does not change.
This is a common problem (gets asked every couple of days). To make long story short, inputs inside ui:repeat do not work, period.
It is a problem with JSF, a long standing, famous one. Maybe it will be fixed. Maybe not, it seems that no one really cares (I mean - an input? in a... ui:repeat? such crazy scenario!).
A quick-fix is to use a h:dataTable, possibly ungodly abused with css to make it look like a list. A long-fix is to use some iterator from a different library. Primefaces has an element that should work that renders an unordered list.
thanks for your replies. Sorry for forget sharing the solution.
As i mentioned above i have primefaces datatable.
On row selection event i render datatable and want to update the cells of that datatable.
USING p:inputtext easily solved my problem. Now i can change the data on screen and i can see the values after update operation on screen. I don't understand the reason but it works.
<p:dataTable var="orgPrmBean"
value="#{scoreCardOperationsBean.selectedCard.orgParameterList}"
emptyMessage="#{labels.norecord}"
rowKey="#{orgPrmBean.id}"
>
<p:columnGroup type="header">
<p:row>
<p:column headerText="Parameters" colspan="3" style="text-align:left;width:480;"/>
</p:row>
</p:columnGroup>
<p:column style="text-align:left;width:200px;">
<h:outputText value="#{orgPrmBean.info}"/>
</p:column>
<p:column style="text-align:left;width:180px;">
<p:inputText value="#{orgPrmBean.weight}"
rendered="#{scoreCardOperationsBean.selectedCard.goalEdit}">
<f:convertNumber maxFractionDigits="0"/>
</p:inputText>
</p:column>
</p:dataTable>
It IS possible to make it work, but the solution is to bind the inputs to a backing bean, and update the values of the controls in the backing bean via listeners (using the new value received in the argument). Obviously this isn't a good solution if you have a complex form, as you need to add a listener/control in the backing bean for each control in the page, but it's practical if you just have one or two inputs.

Resources