FileDownloader 和复选框,下载选中的项目
FileDownloader and checkbox, download selected items
我们创建了解决方案,其中用户有一个 table 文件,每个条目都有复选框。他可以 select 任意数量,然后单击下载按钮。
我们正在使用这样的资源,它应该允许动态下载,具体取决于 selected 项目
private StreamResource createResource(final IndexedContainer container) {
return new StreamResource(new StreamSource() {
@Override
public InputStream getStream() {
for (Object o : container.getItemIds()) {
CheckBox checkbox = (CheckBox) container.getItem(o).getItemProperty(C_CHECK_BOX).getValue();
if (checkbox.getValue()) {
selectedFiles.add(o);
}
}
// do some magic to get stream of selected files
}
}, "download.zip");
}
问题是只有第二次和随后的按钮点击给出了预期的结果。
事实证明,FileDownoader 正在从服务器获取资源,然后发送组件的当前状态。这就是第一次点击给出陈旧结果的原因。
你知道如何克服这个问题吗?是否可以强制:先更新组件再下载资源?
非常感谢
帕维尔
FileDownloader 不适合您的需要。正如您在文档中看到的那样:
Download should be started directly when the user clicks e.g. a Button without going through a server-side click listener to avoid triggering security warnings in some browsers.
这意味着您无法动态生成由复选框值决定的 download.zip
文件,因为这需要访问服务器。
您至少有 2 个选项。每次用户对复选框进行更改时,要么创建新的 FileDownloader
并生成新的 Resource
download.zip
。或者您可以使用这行代码向您的 Button 添加简单的 ClickListener
:
getUI().getPage().open(resource, "_blank", false);
相关:Vaadin - How to open BrowserWindowOpener from a SINGLE BUTTON
默认情况下,Vaadin 中的 CheckBox 是非立即的,这意味着当复选框在浏览器上被选中(或取消选中)时,它不会向服务器发送请求。即时组件也将排队的非即时事件发送到服务器,但 FileDownloader 似乎不会导致将非即时复选框值发送到服务器的事件。
您唯一需要做的就是在创建这些复选框时将其设置为立即显示:
checkBox.setImmediate(true);
还有另一种解决方案来设置 checkBox.setImmediate(true);
。可以在单击之前发送所有组件的当前状态,而不是发送每个复选框更改。
此解决方案基于此答案:
您需要创建文件下载器按钮并将其隐藏:
Button hiddenButton = new Button();
hiddenButton.setId(HIDDEN_ID);
hiddenButton.addStyleName("InvisibleButton");
StreamResource zipResource = createResource(container);
FileDownloader fd = new FileDownloader(zipResource);
fd.extend(hiddenButton);
将 css 规则添加到您的主题
.InvisibleButton {
display: none;
}
然后创建另一个按钮,第一次更新状态,然后点击隐藏按钮。
Button zipDownload = new Button("Download as ZIP file");
zipDownload.addClickListener(new Button.ClickListener() {
@Override
public void buttonClick(Button.ClickEvent event) {
Page.getCurrent().getJavaScript()
.execute(String.format("document.getElementById('%s').click();", HIDDEN_ID));
}
});
我们创建了解决方案,其中用户有一个 table 文件,每个条目都有复选框。他可以 select 任意数量,然后单击下载按钮。
我们正在使用这样的资源,它应该允许动态下载,具体取决于 selected 项目
private StreamResource createResource(final IndexedContainer container) {
return new StreamResource(new StreamSource() {
@Override
public InputStream getStream() {
for (Object o : container.getItemIds()) {
CheckBox checkbox = (CheckBox) container.getItem(o).getItemProperty(C_CHECK_BOX).getValue();
if (checkbox.getValue()) {
selectedFiles.add(o);
}
}
// do some magic to get stream of selected files
}
}, "download.zip");
}
问题是只有第二次和随后的按钮点击给出了预期的结果。
事实证明,FileDownoader 正在从服务器获取资源,然后发送组件的当前状态。这就是第一次点击给出陈旧结果的原因。
你知道如何克服这个问题吗?是否可以强制:先更新组件再下载资源?
非常感谢 帕维尔
FileDownloader 不适合您的需要。正如您在文档中看到的那样:
Download should be started directly when the user clicks e.g. a Button without going through a server-side click listener to avoid triggering security warnings in some browsers.
这意味着您无法动态生成由复选框值决定的 download.zip
文件,因为这需要访问服务器。
您至少有 2 个选项。每次用户对复选框进行更改时,要么创建新的 FileDownloader
并生成新的 Resource
download.zip
。或者您可以使用这行代码向您的 Button 添加简单的 ClickListener
:
getUI().getPage().open(resource, "_blank", false);
相关:Vaadin - How to open BrowserWindowOpener from a SINGLE BUTTON
默认情况下,Vaadin 中的 CheckBox 是非立即的,这意味着当复选框在浏览器上被选中(或取消选中)时,它不会向服务器发送请求。即时组件也将排队的非即时事件发送到服务器,但 FileDownloader 似乎不会导致将非即时复选框值发送到服务器的事件。
您唯一需要做的就是在创建这些复选框时将其设置为立即显示:
checkBox.setImmediate(true);
还有另一种解决方案来设置 checkBox.setImmediate(true);
。可以在单击之前发送所有组件的当前状态,而不是发送每个复选框更改。
此解决方案基于此答案:
您需要创建文件下载器按钮并将其隐藏:
Button hiddenButton = new Button();
hiddenButton.setId(HIDDEN_ID);
hiddenButton.addStyleName("InvisibleButton");
StreamResource zipResource = createResource(container);
FileDownloader fd = new FileDownloader(zipResource);
fd.extend(hiddenButton);
将 css 规则添加到您的主题
.InvisibleButton {
display: none;
}
然后创建另一个按钮,第一次更新状态,然后点击隐藏按钮。
Button zipDownload = new Button("Download as ZIP file");
zipDownload.addClickListener(new Button.ClickListener() {
@Override
public void buttonClick(Button.ClickEvent event) {
Page.getCurrent().getJavaScript()
.execute(String.format("document.getElementById('%s').click();", HIDDEN_ID));
}
});