问题
In the Vaadin example of how to print a dynamically generated PDF they have a two step approach, first you click on the OkButton
and then the PrintButton
. The problem is that their code to open the PDF relies on creating a new BrowserWindowOpener
which then extends the Print
button in the ClickListener
for the OKButton
. In other words they have:
okButton.addClickListener(new ClickListener()
{
@Override
public void buttonClick(ClickEvent event)
{
// Dynamically generate PDF - this is greatly simplified.
StreamResource pdfResource = new PdfStreamResource();
BrowserWindowOpener opener = new BrowserWindowOpener(pdfResource);
opener.extend(printButton);
}
}
This is great because the Print
button is now linked to BrowserWindowOpener
and will immediately open the window/tab when you click on the PrintButton
.
The problem I have is that I don't want an OkButton
, I just want to have a PrintButton
. In this case I can't add
printButton.addClickListener(new ClickListener()
{
@Override
public void buttonClick(ClickEvent event)
{
// Dynamically generate PDF - this is greatly simplified.
StreamResource pdfResource = new PdfStreamResource();
BrowserWindowOpener opener = new BrowserWindowOpener(pdfResource);
opener.extend(printButton);
}
}
In the ClickListener
FOR THE PrintButton
because the first time you click it wont work. I then thought what if I had BrowserWindowOpener
declared outside of the ClickListener
, in the class that links the PrintButton
and ClickListener
, using something a dummy resource:
BrowserWindowOpener opener = new BrowserWindowOpener("");
opener.extend(printButton);
PrintClickListener printClickListener = new PrintClickListener(opener);
printButton.addClickListener(printClickListener);
And then pass the opener
to the ClickListener
so that I could do something like:
printButton.addClickListener(new ClickListener()
{
@Override
public void buttonClick(ClickEvent event)
{
// Dynamically generate PDF - this is greatly simplified.
StreamResource pdfResource = new PdfStreamResource();
opener.setResource(pdfResource);
}
}
However the issue here is that the first click generates a blank page.
My question is how can I get the example working where the dynamically generated PDF is pushed to the browser window with just a SINGLE BUTTON. All the examples for BrowserWindowOpener
seem to always use two buttons. And the examples that declare it higher in the class hierarchy don't work for me, it's always an initial blank page.
Lastly, I would love to be able to do all this within the ClickListener
, that is totally decoupled from everything else.
回答1:
Get rid of BrowserWindowOpener
and use UI.getPage().open()
to view your pdf file.
getUI().getPage().open(resource, "_blank", false);
Notice that this method is deprecated.
As of Vaadin 7.0.0, the functionality for opening a Resource in a Page has been replaced with similar methods based on a String URL. This is because the usage of Resource is problematic with memory management and with security features in some browsers. Is is recommended to instead use Link for starting downloads.
Don't think it is possible to use BrowserWindowOpener
in one button only without hacking in JavaScript.
EDIT: I've achieved desired effect using BrowserWindowOpener. I've extended the class and added pdf generation every time when BrowserWindowOpenerState is requested. The thing is that I've searched for a method that is invoked after the click and before BrowserWindowOpener opens up new tab. The only method I could find is getState() - and only when the state of the component is dirty. I had to also add a flag in overriden getState method because:
setResource method invoke getState() method too, so I had infinite loop
to generate pdf only when its really needed
public class BrowserWindowOpenerWithBeforeClick extends BrowserWindowOpener { private boolean pdfGenerated = false; public BrowserWindowOpenerWithBeforeClick(String url) { super(url); } @Override protected BrowserWindowOpenerState getState(boolean markAsDirty) { if(!pdfGenerated){ StreamResource resource = ((RfgUI) UI.getCurrent()).getResource(); pdfGenerated = true; this.setResource("url",resource); } return (BrowserWindowOpenerState) super.getState(markAsDirty); } public void setPdfGenerated(boolean generated){ this.pdfGenerated = generated; } }
And pdf generation in your main UI class:
void pdfgeneration(final VerticalLayout layout)
{
name.setValue("Slartibartfast");
final Button ok = new Button("OK");
ok.setId("ok-button");
final BrowserWindowOpenerWithBeforeClick bwo = new BrowserWindowOpenerWithBeforeClick("");
bwo.extend(ok);
name.addValueChangeListener(new ValueChangeListener()
{
@Override
public void valueChange(ValueChangeEvent event)
{
bwo.setPdfGenerated(false);
bwo.markAsDirty();
}
});
layout.addComponent(name);
layout.addComponent(ok);
}
And getResource method:
public StreamResource getResource()
{
StreamSource source = new MyPdfSource((String) name.getValue());
String filename = new Random().nextInt() + "pdf_printing_example.pdf";
StreamResource resource = new StreamResource(source, filename);
resource.setMIMEType("application/pdf");
resource.setCacheTime(0);
resource.getStream().setParameter("Content-Disposition", "attachment; filename=" + filename);
return resource;
}
Notice that I had to change the filename everytime I generate new pdf file to avoid caching problems.
来源:https://stackoverflow.com/questions/31224728/vaadin-how-to-open-browserwindowopener-from-a-single-button