Primefaces datatable custom pagination not updating - jsf

I am working on an application where we have our older code base (Struts 1) and are now building out a newer code base using JSF to run alongside the rest of the (old) application. The older datatables that we have across our application show the following for pagination:
"Showing 1 to 20 of 50 entries"
I have been tasked with getting the new PF datatable to display the same way.
I was able to get this working for the first page by overriding the encodePaginatorMarkup method of the primefaces DataRenderer class. The problem comes when the page is changed - this method is not being called to update the pagination. This is confusing to me because the current page (and everything else) gets updated in the normal pagination template without this method getting called.
I looked at the AJAX response being sent back when changing pages and don't see any HTML generated for updating the pagination (only the total count). This leads me to believe that everything is being handled in JS.
The way I'm currently thinking of implementing it would be to use an f:ajax for changing the page, and in the oncomplete pass the widgetVar value for the table and have a common method that will update the pagination based on properties of that datatable in JS. This seems like overkill and I don't want to over-complicate the code if I don't have to.
Is there any better way to go about this? Does anyone have any other suggestions?

Well since there is no other input I went ahead and implemented it using the page event:
<p:dataTable id="users" widgetVar="userTable" paginator="true" rows="20" sortBy="userId">
<p:ajax event="page" oncomplete="setTimeout(function() { update_pagination('userTable'); }, 0)" />
...
</p:dataTable>
The setTimeout was required because the JS object was still not updated when called from oncomplete - so I bumped it into the next event loop, where the values are up-to-date.
Here is the update_pagination JS function:
function update_pagination(table) {
table = PF(table);
var paginator = table.cfg.paginator;
var $pageStart = $(table.jq).find('.paginator_pagestart');
var $pageEnd = $(table.jq).find('.paginator_pageend');
$pageStart.html(paginator.page*paginator.rows+1);
$pageEnd.html(Math.min(paginator.rowCount, paginator.page*paginator.rows+paginator.rows));
}
I pass it the table name, grab the actual PF object and then use the paginator part of the cfg to update the current entries being shown. I use Math.min because on the last page it might not have enough data, and I didn't want it reading "Showing 1 to 20 of 7 entries", but rather "Showing 1 to 7 of 7 entries"

Related

Primefaces/JSF functionality not working on updating dynamic columns

OK, so I'll express what I'm doing in bullet points without code (as I'm in a massive muddle)...
Using a single-page application with <p:layout> consisting of <p:layoutUnit>s (in different pages brought to main page using <ui:include>)
One of these <p:layoutUnit>s is a single <p:dataTable> updating dynamically (also it's a dynamic dataTable based on selected single-row and associated <p:commandButton>
Rest of the <p:layoutUnit>s update dynamically based on actions happening in dataTable (they contain buttons whose actions (presently not defined) update with change in dataTable data and dynamic 'header text')
dataTable uses dynamic columns (as data to be shown changes, if it helps, data is in a 'tree structure' and I navigate 'up' and 'down using buttons (common 'back' in header) and 'next' <p:commandButton> for each row)
Now to the actual problem
When I use the 'next' button to go to the next level of data in the tree, let me illustrate what happens:
Root -> 'Next' -> Goes to Child -> data updated
Child -> 'Next' -> No action -> Use one of the non-dataTable buttons with no function defined -> default JSF/PF action is Page Refresh -> data updated
'Next' -> Goes to Child
Why does this happen? Why does the 'no action' period happen in between? I feel like I'm missing something here. It's never happened before. None of the controller's functions are triggerable at that time. I've checked this by using the debugger in my IDE. I'm 100% sure it's a problem with the dynamic dataTable. But I don't understand why as I've initiated Column Controller in the 'preRenderView' stage using <f:event>
Also using an 'id' for the body in the Main Index Page and updating using RequestContext just makes the page blank.
I've tried multiple combinations of View and Request Scopes for the Column Controller and used a 'PreViewRender' event listener in the dataTable to initiate the Column Controller. All of my efforts.
One particular combination (IIRC **NOT ** using the PreViewRender listener causing even the data to not be updated in the dataTable, forget non-functioning of <p:commandButton>s or <p:remoteCommand> on <a> onclicks.
As there is a huge amount of code spanning this and am in a muddle myself now, I'll add code when you make some headway.
OK, I found the answer!
https://stackoverflow.com/a/2120183/3488113
This is user BalusC's answer where one of the main reasons he suggests is JS error.
I am calling a JavaScript function from the Bean which does not exist/returns some error. Removing references to it in the Bean has solved the problem for the time-being.
I assume the rest of the update actions stop/frontend component JS action changes are not loaded/actions itself are not loaded if there is a JS error in the backend call, which is the real cause.

Using binding attribute causes javax.faces.FacesException: Cannot find component with identifier

I have a problem I can't quite get a handle on.
First the context: I am developing a web application using Primefaces 3.5 (yes, unfortunately I am stuck with this old version for now), running on JBoss 7.
There is a form with id "form" encompassing all following xhtml code.
I have a component in my view which is provided by usage of the binding attribute:
<p:dashboard id="dashboard" binding="#{myBackingBean.dashboard}" />
Then sometimes I would like to perform an ajax update on this component, this is done by using the RemoteCommand component of primefaces:
<p:remoteCommand
actionListener="#{myBackingBean.someActionListener()}"
process="#this" id="myRmtCmd" oncomplete="myJsFunction();"
update=":form:dashboard" name="myRemoteCommand" />
The RemoteCommand is triggered by a clicking on a Link:
Some Text
This works pretty well so far. However after deploying this code to production I sometimes get a FacesException:
javax.faces.FacesException: Cannot find component with identifier ":form:dashboard"
referenced from "form:myRmtCmd".
This is where my problem lies because I cannot reliably reproduce this exception. My question is this: What could lead to this exception being thrown? It seems to work 95 % of the time but being the perfectionist I am (and many of you reading this are as well, I'm sure ;) ) I would like this code to work 100 % of the time. What am I missing?
Before answering please consider these constraints:
yes, i have to use the binding attribute for providing the dashboard as I need a great deal of control over what gets added to the component
to avoid using IDs I also tried updating the dashboard by its css class via one of primefaces' advanced selectors: #(.ui-dashboard) - this also does not work!
yes, it would be possible to use a commandbutton/link instead of wiring up the remotecommand component to a simple html link but in this case the link is rendered by a JSF renderer component and I made some bad experiences with dynamically adding buttons etc (due to JSF Spec Issue 790)
Cheers,
p.s.
I also had this weird behavior.
There are probably more than one component bindded to #{myBackingBean.dashboard}, so the first one sets the id and there will be no one called "dashboard".

Request update of component with ajax from LazyDataModel.load?

I'm using PrimeFaces version 3.5.
I want to implement the message informing that the filter in the DataTable have return too many rows and only first 50 will be displayed.
I've tried to do it with both Messages and outputText. I set the text to display and request update:
RequestContext context = RequestContext.getCurrentInstance();
context.update("form:message");
context.update("form:text");
message = "Too many rows";
The components:
<p:message id="message" for="message"/>
<h:outputText id="text" value="#{userPicker.model.message}"/>
However, I don's see the proper update sections in partial-request response. Is it possible to request the update of components from within the LazyDataModel.load function (and if, what I'm doing wrong here)? If it is not possible (why?) how else can I force refresh of element from DataTable filtering?
The load method of LazyDataModel is invoked in render phase, when it's too late to add additional components to update.
However, it is possible to add JavaScript execution command:
context.execute("show_message()");
Where function show_message will display the message on client side, which enables to accomplish the task with single request.

SelectManyCheckbox onHide with remoteCommand

I ran into a very interesting issue. Here is my scenario:
My Goal
Use a SelectManyCheckbox with a nested tooltip.
Use SelectManyCheckbox onHide event to fire an Ajax (ActionListener) call
and update the
SelectManyCheckbox label and nested tooltip text.
My Approach
Use a remoteCommand and tie it to the SelectManyCheckbox onHide event
XHTML
<p:selectCheckboxMenu id="sourceFilter"
onHide="sourceFilterCommand();"
value="#{viewRevenueBean.sourceSelectManyMenu.selectedValues}"
label="#{viewRevenueBean.sourceSelectManyMenu.label}"
filter="true" filterMatchMode="contains"
validator="#{viewRevenueBean.sourceSelectManyMenu.validate}"
widgetVar="srcFilterDropDown">
<f:selectItems id="sourceItems"
value="#{viewRevenueBean.sourceSelectManyMenu.availableItems}"
var="source" itemLabel="#{source.label}" itemValue="#{source.value}" />
<f:convertNumber type="number" />
<p:tooltip id="srcToolTip"
for="sourceFilter"
value="#{viewRevenueBean.sourceSelectManyMenu.tooltipText}"
showEffect="fade"
hideEffect="fade"/>
<p:remoteCommand name="sourceFilterCommand" update="sourceFilter"
actionListener=#{viewRevenueBean.sourceSelectManyMenu.defaultEventHandler}"/>
</p:selectCheckboxMenu>
My Results
Ajax (Action Listener) gets fired and SelectManyCheckbox label and nested tooltip are updated (expected behavior).
In Firebug, I noticed that each onHide event Ajax call is multiplying the preceding number of server side requests by two (unexpected behavior).
e.g
1st onHide event = 1 Request
2nd onHide event = 2 Requests
3rd onHide event = 4 Requests
4th onHide event = 8 Requests
5th onHide event = 16 Requests
etc.....
This is obviously not desired and leads to a big slow down after just
a couple onHide events.
Experiments I tried
I created a p:command button which accomplished the desired Ajax call and correct element updates (without the multiplied request
issue) . I then proceeded to steal it's Ajax JavaScript call via
Firebug and placed it in my own JavaScript function, which I then
used as my onHide callback. Again, I experience the same unwanted
result, the label and tooltip are updated, but the requests start to
multiply.
I tried placing the remoteCommand in different locations
(outside the menu, inside it's own form etc). It doesn't make a
difference. The problem is still encountered.
I tried simplifying the SelectManyCheckbox scenario (remove
tooltip, coverter, tweak various attributes etc) to eliminate other
possibilities. No difference.
I tried a p:ajax instead of p:remoteCommand using onchange.
The Ajax requests work fine but obviously it's not what I am after.
I need to trigger it onHide.
Instead of a SelectCheckboxMenu , i tried using a
SelectManyCheckbox (no label) with onchange and keeping everything
else the same. The remoteCommand works fine, the Ajax call gets
called once and everything is nice and dandy. [/list] [list] * I
tried the PrimeFaces 3.5-SNAPSHOT as well. No difference. Issue is
still manifested.
Haven't found any clues on the forum or the net thus far in regards
to this issue. Does this sound like a bug or programmer clumsiness
:roll: ? Of course any insight and/or suggestions are highly
appreciated.
I have run into similar problems when using p:remoteCommand. I can't say for sure that the root cause is the same in your case, but maybe this can help somewhat.
In my case the problem was caused by multiplied registering of jquery bindings; the p:remoteCommand seem not to use $(somesource).off("some_event").on("some_event", some_function). That means - as far as I have understood - that if you update the component containing the p:remoteCommand, it's action will be registered over and over again, each time it's being updated. That in turn will mean that if you call on the name of the p:remoteCommand, it will fire the same amount of times as it's been registered.
You said you'd tried to move it outside and still got the same problem, so maybe it's not this problem after all. In my case I tested this assumption using a p:commandLink instead and had that call the backing bean. The goal for me was to make sure that any previous registration of a binding was removed, so through registering the binding like mentioned above:
$(somesource).off("some_event").on("some_event", some_function), and let some_function click the link, you can at least check if it solves the problem.

Can't update component in p:dataTable (not whole p:dataTable) in Primefaces

I'm using Primefaces 3.3.1 with Tomcat 7.0.22.0.
I have p:dataTable which has p:inputText in it. The id of that p:dataTable is "houseTabID:tabView:form0:table". This is copy-pasted from HTML source. And I let dialog (which is outside of the form dataTable resides) open by button in p:dataTable, and p:commandButton in that dialog updates the dataTable.
p:commandButton looks like this;
<p:commandButton ajax="true" action="#{myBean.setInputText()}"
value="OK"
update="#{myBean.getUpdateTarget(0)}"/>
myBean.getUpdateTarget(0) retruns proper string to point target component.
I can successfully update whole dataTable by specifying "houseTabID:tabView:form0:table" (meaning myBean.getUpdateTarget(0) returns that string). But I have many lines in the table so updating whole table takes longer to finish, and scroll position is reset which is really irritating. That makes me want to update only one row in the table, not whole.
So I first returned "houseTabID:tabView:form0:table:inputBox" to update p:inputText that I want to update. In p:dataTable I have something looks like this;
<p:column headerText="Value"
width="300" style="height:16px; font-size:9pt;">
<p:inputText id="inputBox" value="#{myItem.value}"
style="width:95%; height:11px; font-size:9pt;">
<f:ajax execute="#this" event="blur" />
</p:inputText>
</p:column>
Result: no update, no error log. I know "houseTabID:tabView:form0:table:inputBox" won't work since it does not specify row in table. So by using rowIndexVar, I tried with "houseTabID:tabView:form0:table:0:inputBox" with making :0: part from rowIndexVar. The string is copy-pasted from HTML source. But unfortunatelly, I got exception.
javax.faces.FacesException: Cannot find component with identifier ":houseTabID:tabView:form0:table:0:inputBox" referenced from "houseTabID:j_idt181:j_idt186".
Why? There is obviously "houseTabID:tabView:form0:table:0:inputBox", I see it on HTML source, and I can update whole talbe with "houseTabID:tabView:form0:table" but throw exception with "houseTabID:tabView:form0:table:0:inputBox"? It does not make sense to me. Also, then, why it won't throw any exception or spill out error log with "houseTabID:tabView:form0:table:inputBox"? I verified HTML source of that p:commandButton;
onclick="PrimeFaces.ab({source:'houseTabID:j_idt190:j_idt195',update:'houseTabID:tabView:form0:table:inputBox',
I desparately want to update just one row instead of whole p:dataTable. Please help me.
I found a solution. According to this conversation,
position = JQuery(".ui-datatable-scrollable-body").scrollTop();
JQuery(".ui-datatable-scrollable-body").scrollTop(position);
can do the trick. It seems this is the only way to get and set vertical scroll position of p:dataTable. I saved position before opening dialog, and set position after updating p:dataTable from dialog. It worked and this is what I wanted to do.
But above method has serious issue. That code scans given class ".ui-datatable-scrollable-body" from top and perform at the first found one. So it works only for one p:dataTable. Fortunately I could come up with 2nd solution.
<script type="text/javascript">
$ = jQuery;
var scrollPos;
function saveScrollPos()
{
scrollPos = $("#houseTabID\\:tabView\\:form0\\:table_data").parents(".ui-datatable-scrollable-body").scrollTop();
}
function getScrollPos()
{
$("#houseTabID\\:tabView\\:form0\\:table_data").parents(".ui-datatable-scrollable-body").scrollTop(scrollPos);
}
</script>
FireBug in FireFox helped me find out there is componnet id which "_data" is appedded by Primefaces. Please see my question, my data table's id is "table" and someone created "table_data". And I could point it with escaped syntax shown above and function parents() leads to that spot. It is working fine in my applictaion.
I really thank stier01 who posted his solution which is the only one I could find over the net. And I really feel sad that Primefaces p:dataTable does not support such a basic thing. I know many of us wanted to keep scroll position of p:dataTable when updating it. I hope it will be improved in near future.
I am teetering on the edge of what I know here but I suspect that it may be happening because of the timing of the page construction. When the DOM tree is built, your table rows do not yet exist so your button outside of the table cannot find the reference to the row (because it does not yet exist). After the tree is built and the data is filled in, the row is then there. That is why after the page renders you can look up the column id, but not reference it from outside of the table. I am not sure of a solution. Maybe you can put a remote command in the datatable tags to update it. Maybe can do something in JS.

Resources