问题
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 code
document
: Managed BeangetDrawings()
: method return a list of Drawing (entity class)CheckedOutBy
: attribute of Entity Drawing
<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
}
回答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.
回答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;
}
}
}
回答3:
You could use the update
attribute of the p:commandButton
component to re-render the area that you intend to refresh after download, in this case your "drawing_table".
<p:commandButton update="drawing_table" action="#{document.Download}" value="Download" />
回答4:
As Balusc answered, we cannot get response twice from a single request.
To refresh a page after download, better use the following java script in download link(p:commandbutton
) onclick tag.
Example:
<p:commandButton ajax="false" icon="ui-icon-arrowstop-1-s" onclick="setTimeout('location.reload();', 1000);" action="#{managedBean.downloadMethod}" />
this will automatically refresh the page after 1 second, at the same time i.e. before refresh, you will get the download file, based on your download response time, increase the seconds in that script. Seconds should not less than that download response time.
来源:https://stackoverflow.com/questions/3471880/how-to-refresh-a-page-after-download