How to refresh a page after download
Solution 1
You'd basically like to let the client fire two requests. One to retrieve the download and other to refresh the new page. They cannot be done in a single HTTP request. Since the download needs to be taken place synchronously and there's no way to hook on complete of the download from the client side on, there are no clean JSF/JS/Ajax ways to update a component on complete of the download.
Your best JSF-bet with help of PrimeFaces is <p:poll>
<h:outputText id="checkedOutBy" value="#{item.checkedOutBy}"/>
...
<p:poll id="poll" interval="5" update="checkedOutBy" />
or <p:push>
<p:push onpublish="javaScriptFunctionWhichUpdatesCheckedOutBy" />
Polling is easy, but I can imagine that it adds unnecessary overhead. You cannot start it using standard JSF/PrimeFaces components when the synchronous download starts. But you can stop it to let it do a self-check on the rendered
attribute. Pushing is technically the best solution, but tougher to get started with. PrimeFaces explains its use however nicely in chapter 6 of the User Guide.
Solution 2
Well, I decided to go with BalusC's answer/recommendation above, and decided to share my code here for people that may stop by here, 'later'. FYI, my environment details are below:
TomEE 1.6.0 SNAPSHOT (Tomcat 7.0.39), PrimeFaces 3.5 (PrimeFaces Push), Atmosphere 1.0.13 snapshot (1.0.12 is latest stable version)
First of all, i am using p:fileDownload with p:commandLink.
<p:commandLink value="Download" ajax="false"
actionListener="#{pf_ordersController.refreshDriverWorksheetsToDownload()}">
<p:fileDownload value="#{driverWorksheet.file}"/>
</p:commandLink>
Since I have the xhtml above, and since p:fileDownload does not allow oncomplete="someJavaScript()" to be executed, I decided to use PrimeFaces Push to push message to client, to trigger the javascript necessary to unblock the UI, because UI was being blocked whenever I click the commandLink to download file, and for many months, I didn't know how to solve this.
Since I already am using PrimeFaces Push, I had to tweak the following on the client side:
.js file; contains method that handles messages pushed from server to client
function handlePushedMessage(msg) {
/* refer to primefaces.js, growl widget,
* search for: show, renderMessage, e.detail
*
* sample msg below:
*
* {"data":{"summary":"","detail":"displayLoadingImage(false)","severity":"Info","rendered":false}}
*/
if (msg.detail.indexOf("displayLoadingImage(false)") != -1) {
displayLoadingImage(false);
}
else {
msg.severity = 'info';
growl.show([msg]);
}
}
index.xhtml; contains p:socket component (PrimeFaces Push); i recommend all of the following, if you're are implementing FacesMessage example of PrimeFaces Push
<h:outputScript library="primefaces" name="push/push.js" target="head" />
<p:growl id="pushedNotifications" for="socketForNotifications"
widgetVar="growl" globalOnly="false"
life="30000" showDetail="true" showSummary="true" escape="false"/>
<p:socket id="socketForNotifications" onMessage="handlePushedMessage"
widgetVar="socket"
channel="/#{pf_usersController.userPushChannelId}" />
Months (or maybe a year-or-so) ago, i found it necessary to add the following to the commandLink that wraps p:fileDownload, which will refresh the file/stream on server, so you can click the file as many times as you need and download the file again and again without refreshing the page via F5/refresh key on keyboard (or similar on mobile device)
actionListener="#{pf_ordersController.refreshDriverWorksheetsToDownload()}"
That bean method is referenced whenever enduser clicks the commandLink, to download file, so this was perfect spot to 'push' a message from server to client, to trigger javascript on client, to unblock UI.
Below are the bean methods in my app that gets the job done. :)
pf_ordersController.refreshDriverWorksheetsToDownload()
public String refreshDriverWorksheetsToDownload() {
String returnValue = prepareDriverWorksheetPrompt("download", false);
usersController.pushNotificationToUser("displayLoadingImage(false)");
return returnValue;
}
usersController.pushNotificationToUser(); i had to add this one, tonight.
public void pushNotificationToUser(String notification) {
applicationScopeBean.pushNotificationToUser(notification, user);
}
applicationScopeBean.pushNotificationToUser(); this already existed, no change to this method.
public void pushNotificationToUser(String msg, Users userPushingMessage) {
for (SessionInfo session : sessions) {
if (userPushingMessage != null &&
session.getUser().getUserName().equals(userPushingMessage.getUserName()) &&
session.getUser().getLastLoginDt().equals(userPushingMessage.getLastLoginDt())) {
PushContext pushContext = PushContextFactory.getDefault().getPushContext();
pushContext.push("/" + session.getPushChannelId(),
new FacesMessage(FacesMessage.SEVERITY_INFO, "", msg));
break;
}
}
}
Related videos on Youtube
Thang Pham
Updated on June 04, 2022Comments
-
Thang Pham almost 2 years
I have a commandButton that will invoke a function to download a file (standard stuffs like
InputStream
,BufferedOutputStream
...) After download success, at the end of the function, I change some values of the current object and persist it into database. All of these work correctly. Now when file is done downloading, the content of the page is not updated. I have to hit refresh for me to see updated content. Please help. Below are the basic structure of my codedocument
: Managed Bean
getDrawings()
: method return a list of Drawing (entity class)
CheckedOutBy
: attribute of EntityDrawing
<p:dataTable id="drawing_table" value="#{document.drawings}" var="item" > <p:column> <f:facet name="header"> <h:outputText value="CheckedOutBy"/> </f:facet> <h:outputText value="#{item.checkedOutBy}"/> ... </p:dataTable> <p:commandButton ajax="false" action="#{document.Download}" value="Download" />
Inside my Managed Bean
public void Download(){ Drawing drawing = getCurrentDrawing(); //Download drawing drawing.setCheckedOutBy("Some Text"); sBean.merge(drawing); //Update "Some Text" into CheckedOutBy field }
-
BalusC over 13 yearsIt's because of
ajax="false"
(which is mandatory to get the file to download). -
Thang Pham over 13 yearsthank you for your input. I am reading the chapter now. I will get back to you if I have more question. Thanks for edit my post, switch from
h:commandButton
top:commandButton
. I originally have it asp:commandButton
, but I was worry that many people would not understand it, sinceprimefaces
tag only have38
rating. -
BalusC over 13 yearsThis isn't a rating, this is just the amount of tagged questions as far. As long as you tag
jsf
along it, it'll be spotted by the right people ;) -
Thang Pham over 13 yearsI am still a bit vague. So does my
p:commmandButton
become this<p:commandButton ajax="false" action="#{document.Download}" actionListener=”#{document.update}” value="Download" />
. My entity Drawing have a booleanchecked
attribute which essentially tell me whichdrawing
the user select. So it could be multiples drawings select to be downloaded. Therefore, myupdate
method on the server, I dont have a clear idea of what topush
down to the browser. Do you think I should put down the entire column ofcheckedOutBy
, or somehow figure out which one need to be updated, and push them -
BalusC over 13 yearsSend a delimited/formatted string which you can process further in JS code. JSON format may be highly suitable here.
-
Thang Pham over 13 yearsCurrently, I dont think push is supported in Glassfish, according to people from primefaces forum. I guess poll is my only option. Do u mind elaborate what u said earlier:
You cannot start it using standard JSF/PrimeFaces components when the synchronous download starts. But you can stop it to let it do a self-check on the rendered attribute.
-
BalusC over 13 yearsSomething like
<p:poll id="poll" interval="5" update="checkedOutBy poll" rendered="#{!downloadFinished}" />
-
Thang Pham over 13 yearsI see once you done downloading, u set the boolean
downloadFinished
. You cannot start poll when the synchronous download start because the download use up the only one HTTP request -
Thang Pham over 13 yearsI did not like
polling
too much, since it keep refresh the table when it does not need too. I redesign, so that it took me to a confirm page, so when the user done downloading, they clickBack
to return back to the list, at which point, a new Http request is created and refresh the page. I marked this your post as solution, since itpush
andpoll
actually work well, just not for me :D -
Howard about 11 yearsOkay, I'm going to +1 this question and BalusC's answer (of course). Great recommendation, BalusC, about using push or poll to do some javascript on the client/page to refresh the page. @ThangPham, yes, there were issues with PrimeFaces Push and Glassfish 3.x, but Glassfish 3.1.2.2 works fine with PrimeFaces Push (PrimeFaces 3.5) and Atmosphere 1.0.12 (runtime, jbossweb, tomcat, and tomcat7 JARs; those are the only JARs you need along with Glassfish 3.1.2.2, and look for Glassfish 3.1.2.2 topic in PrimeFaces Push forum). I'm going to try this to solve an issue I was having with p:fileDownload