我有一个命令按钮,它将调用一个函数来下载文件(标准的东西,如
InputStream
,BufferedOutputStream
...)下载成功后,在函数结束时,我更改当前对象的一些值并保留将其存入数据库。所有这些都可以正常工作。现在,当文件下载完成时,页面内容不会更新。我必须点击刷新才能看到更新的内容。请帮忙。以下是我的代码的基本结构
document
:托管 BeangetDrawings()
:方法返回Drawing列表(实体类)CheckedOutBy
:实体的属性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" />
在我的托管 Bean 中
public void Download(){
Drawing drawing = getCurrentDrawing();
//Download drawing
drawing.setCheckedOutBy("Some Text");
sBean.merge(drawing); //Update "Some Text" into CheckedOutBy field
}
PrimeFaces.monitorDownload()
并在结束回调中调用 location.reload()
。
<p:commandButton ... onclick="PrimeFaces.monitorDownload(null, stopDownload)">
<p:fileDownload value="#{fileDownloadController.file}"/>
</p:commandButton>
function stopDownload() {
location.reload();
}
如果您尚未使用 PrimeFaces 4.x,请升级。
好吧,我决定采用上面 BalusC 的答案/建议,并决定在这里分享我的代码,以便“稍后”可能会在这里停留的人们。仅供参考,我的环境详细信息如下:
TomEE 1.6.0 SNAPSHOT (Tomcat 7.0.39)、PrimeFaces 3.5 (PrimeFaces Push)、Atmosphere 1.0.13 快照(1.0.12 是最新稳定版本)
首先,我使用 p:fileDownload 和 p:commandLink。
<p:commandLink value="Download" ajax="false"
actionListener="#{pf_ordersController.refreshDriverWorksheetsToDownload()}">
<p:fileDownload value="#{driverWorksheet.file}"/>
</p:commandLink>
由于我有上面的 xhtml,并且 p:fileDownload 不允许执行 oncomplete="someJavaScript()",所以我决定使用 PrimeFaces Push 将消息推送到客户端,以触发解锁 UI 所需的 JavaScript,因为每当我单击命令链接下载文件时,UI 就会被阻止,几个月来我都不知道如何解决这个问题。
由于我已经在使用 PrimeFaces Push,我必须在客户端进行以下调整:
.js 文件;包含处理从服务器推送到客户端的消息的方法
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;包含 p:socket 组件(PrimeFaces Push);如果您要实现 PrimeFaces Push 的 FacesMessage 示例,我推荐以下所有内容
<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}" />
几个月(或者大约一年)前,我发现有必要将以下内容添加到包装 p:fileDownload 的 commandLink 中,这将刷新服务器上的文件/流,以便您可以多次单击该文件根据需要一次又一次下载文件,无需通过键盘上的 F5/刷新键(或移动设备上的类似键)刷新页面
actionListener="#{pf_ordersController.refreshDriverWorksheetsToDownload()}"
每当最终用户单击 commandLink 下载文件时,都会引用该 bean 方法,因此这是将消息从服务器“推送”到客户端、在客户端上触发 javascript、解锁 UI 的完美位置。
下面是我的应用程序中完成工作的 bean 方法。 :)
pf_ordersController.refreshDriverWorksheetsToDownload()
public String refreshDriverWorksheetsToDownload() {
String returnValue = prepareDriverWorksheetPrompt("download", false);
usersController.pushNotificationToUser("displayLoadingImage(false)");
return returnValue;
}
usersController.pushNotificationToUser();今晚我必须添加这个。
public void pushNotificationToUser(String notification) {
applicationScopeBean.pushNotificationToUser(notification, user);
}
applicationScopeBean.pushNotificationToUser();这已经存在,无需更改此方法。
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;
}
}
}
您可以使用
update
组件的 p:commandButton
属性来重新渲染下载后要刷新的区域,在本例中是您的“drawing_table”。
<p:commandButton update="drawing_table" action="#{document.Download}" value="Download" />
我还选择了@Balusc建议的投票方式。 为了将开销降至最低,我仅在按下按钮后才开始轮询(autoStart =“false”):
<h:form id='formOne'>
<h:selectOneMenu id="checkedOutBy" value="#{bean.items}".../>
....
<p:commandButton value="Download PDF" action="#{controller.downloadPdf}" onclick="startPolling();" ajax="false" />
....
<p:poll interval="5" update="checkedOutBy" autoStart="false" widgetVar="pollWidget" oncomplete="checkPollCount()"/>
</h:form>
并为被调用函数添加此 JavaScript checkPollCount 和 startPolling:
<script type="text/javascript">
let pollCount = 0;
const maxPollCount = 10; // max Poll-tries
function checkPollCount() {
var value= $('#formOne\\:checkedOutBy:selected').val();
if (value === 'expectedValue after Change') {
PF('pollWidget').stop();
return;
}
pollCount++;
if (pollCount >= maxPollCount) {
PF('pollWidget').stop();
pollCount = 0;
}
}
function startPolling() {
pollCount = 0;
PF('pollWidget').start();
}
</script>
正如 Balusc 所回答的那样,我们无法从单个请求中获得两次响应。
要在下载后刷新页面,最好在下载链接中使用以下java脚本(
p:commandbutton
)onclick标签。
示例:
<p:commandButton ajax="false" icon="ui-icon-arrowstop-1-s" onclick="setTimeout('location.reload();', 1000);" action="#{managedBean.downloadMethod}" />
这将在1秒后自动刷新页面,同时即在刷新之前,您将获得下载文件,根据您的下载响应时间,增加该脚本中的秒数。 秒数不应小于下载响应时间。