JSF + Primefaces + StreamedContent + documentViewer + ajax - ajax

I'm using Primefaces 5.2 with extensions 3.1.
I have my datatable and on click on each row I'd like to display modal and in this modal use documentViewer which will display document based on parameter passed from selected row.
With this I'm invoking my modal from within datatable
<p:ajax event="rowSelect" update=":previewDataForm" oncomplete="$('.previewDataModal').modal();" immediate="true">
<f:param name="pdfFile" value="#{row.dataPath}"/>
</p:ajax>
this is my modal:
<b:modal id="previewDataModal" title="Preview" styleClass="orderPreviewModalPseudoClass">
<h:form id="previewDataForm">
<pe:documentViewer height="550" value="#{contentStreamHelperBean.pdfFromFileSystem}" />
</h:form>
</b:modal>
and this is my stream helper
#Component("contentStreamHelperBean")
#Scope("request")
public class ContentStreamHelperBean extends BaseBean {
private static final Logger log = LoggerFactory.getLogger(ContentStreamHelperBean.class);
public StreamedContent getPdfFromFileSystem() {
String pdfFile = getRequestAttribute("pdfFile");
if (getFacesContext().getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
return new DefaultStreamedContent();
} else {
if (StringUtils.isNotEmpty(pdfFile))
try {
return new DefaultStreamedContent(new FileInputStream(new File(pdfFile)), "application/pdf");
} catch (FileNotFoundException e) {
log.error("Unable to get pdf", e);
}
}
return new DefaultStreamedContent();
}
}
Problem is, that when onclick is invoked I can't get pdfFile param in my ContentStreamHelper so:
how to pass parameter from dataTable rowSelect to my backingBean?
is this good approach how to display PDF with documentViewer?

Related

JSF file download exception handling (how to prevent view rerendering)

Good day, friends!
First of all, I'm sorry for my English. Here is my question:
I'm new to JSF (2.0), and I'm trying to use BalusC algorythm for file download from managed bean. Function works correctly and "Save As..." dialog appears in browser. But I don't know how to return file download error message (exception, DB error etc. in backing bean method) without view reload/redirect.
My hidden button on the view:
<h:form id="detailsDecisionMainForm">
<h:inputHidden id="docId" value="" />
<h:commandButton id="downloadAction" action="#{detailsDecisionGridBean.downloadForm()}" style="display: none;" />
</h:form>
My managed bean (which scope can I use? I've tried request and view scopes) method:
public String downloadForm() {
log.fine("downloadForm call");
PdfService pdfService = new PdfServiceImpl();
ArmCommonService armCommonService = new ArmCommonServiceImpl();
String errorMessage = null;
try {
Long opUni = new Long(FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("detailsDecisionMainForm:docId"));
log.fine("Document opUni = " + opUni);
DocXML docXML = armCommonService.getDocXMLById(opUni);
if (docXML != null) {
File pdfFile = pdfService.getPdfReport(docXML.getXml(), docXML.getType());
if (pdfFile != null) {
DownloadUtils.exportPdf(pdfFile);
} else {
log.log(Level.SEVERE, "downloadForm error: pdf generation error");
errorMessage = "PDF-document generation error.";
}
} else {
log.log(Level.SEVERE, "downloadForm error: xml not found");
errorMessage = "XML-document not found.";
}
} catch (Exception ex) {
log.log(Level.SEVERE, "downloadForm exception: " + ex.getMessage());
errorMessage = "File download exception.";
}
if (errorMessage != null) {
FacesContext.getCurrentInstance().addMessage("detailsDecisionMainForm:downloadAction", new FacesMessage(errorMessage));
}
return null;
}
DownloadUtils.exportPdf() procedure works correctly:
public static void exportPdf(File file) throws IOException {
InputStream fileIS = null;
try {
log.fine("exportPdf call");
fileIS = new FileInputStream(file);
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
ec.responseReset();
ec.setResponseContentType(APPLICATION_PDF_UTF_8);
byte[] buffer = ByteStreams.toByteArray(fileIS);
ec.setResponseContentLength(buffer.length);
ec.setResponseHeader(HttpHeaders.CONTENT_DISPOSITION, String.format(CONTENT_DISPOSITION_VALUE, new String(file.getName().getBytes(StandardCharsets.UTF_8))));
ec.getResponseOutputStream().write(buffer);
fc.responseComplete();
} catch (Exception ex) {
log.log(Level.SEVERE, "exportPdf exception: " + ex.getMessage());
} finally {
if (fileIS != null) {
fileIS.close();
log.fine("exportPdf inputstream file closed");
}
}
}
What can I do to prevent view rerendering after downloadForm() error/exception? And how can I show javascript alert() with message text (in future - jQuery.messager panel with error text)?
Thank you!
In order to prevent a full page reload, you have to submit form by ajax. But, in order to be able to download a file, you have to turn off ajax. This doesn't go well together.
Your best bet is to split the action in two requests. First send an ajax request which creates the file on a temporary location in the server side. When this fails, you can just display a faces message the usual way. When this succeeds, you can just automatically trigger the second request by submitting a hidden non-ajax command button via conditionally rendered JavaScript. This second request can then just stream the already successfully created file from the temporary location to the response.
A similar question was already asked and answered before: Conditionally provide either file download or show export validation error message. But this involves PrimeFaces and OmniFaces. Below is the standard JSF approach:
<h:form id="form">
...
<h:commandButton value="Export" action="#{bean.export}">
<f:ajax ... render="result" />
</h:commandButton>
<h:panelGroup id="result">
<h:messages />
<h:commandButton id="download" action="#{bean.download}"
style="display:none" />
<h:outputScript rendered="#{not facesContext.validationFailed}">
document.getElementById("form:download").onclick();
</h:outputScript>
</h:panelGroup>
</h:form>
And use this #ViewScoped bean (logic is based on your existing logic). Basically, just get hold of the File as instance variable during export action (ajax) and then stream it during download action (non-ajax).
private File pdfFile;
public void export() {
try {
pdfFile = pdfService.getPdfReport(...);
} catch (Exception e) {
context.addMessage(...);
}
}
public void download() throws IOException {
DownloadUtils.exportPdf(pdfFile);
}
See also:
How to invoke a JSF managed bean on a HTML DOM event using native JavaScript?
Store PDF for a limited time on app server and make it available for download

p:calendar AJAX updates only the second time

I have a problem with PrimeFaces calendar and ajax update.
I have a calendar and when I change the date on the UI I want to refresh another component. The problem is that the first time I change the date, the other component stays the same, but when i change the date a second time, the other component is updated with the correct value that I am expecting.
Here is my form:
<h:form>
<p:calendar
value="#{foo.dtValidade}">
<p:ajax update="criteriosDataGridTeste" event="dateSelect"
listener="#{foo.updateTeste}" />
</p:calendar>
<h:panelGroup id="criteriosDataGridTeste">
<td><h:selectOneMenu value="#{foo.idtpresult}">
<f:selectItem itemLabel="ok" itemValue="ok" />
<f:selectItem itemLabel="not ok" itemValue="not ok" />
</h:selectOneMenu></td>
</h:panelGroup>
</h:form>
I am using primefaces 3.0.1
By debugging I found out that the listener is being called before the value dtValidade is set.
But i thought it should be the other way.
Why is this happening?
A simplified version of my bean:
#ManagedBean(name = "foo")
#ViewScoped
public class Foooo {
private Date dtValidade;
private String idtpresult;
public Foooo() {
dtValidade = new Date();
idtpresult = "not ok";
}
public Date getDtValidade() {
return dtValidade;
}
public void setDtValidade(Date dt) {
this.dtValidade = dt;
}
public String getIdtpresult() {
return idtpresult;
}
public void setIdtpresult(String idtpresult) {
this.idtpresult = idtpresult;
}
public void updateTeste() {
Date now = new Date();
if (dtValidade.before(now)) {
idtpresult = "ok";
} else {
idtpresult = "not ok";
}
}
}
Am I missing anything?
Thanks in advance!
try this one
add in xhtml code in the h:form where yours calendar is
<p:remoteCommand name="rc" update="criteriosDataGrid" >
and in your ajax
<p:ajax
event="dateSelect" listener="#{foo.updateCriteria}" oncomplete="rc"/>
i dont know excatly if you need the rest of attributes of p:ajax

PrimeFaces autocomplete with omniFaces.ListConverter and ajax return null in backBean

I'm trying to do a country/state/city loader with autocomplete tag but this don't works. I already had done with SelectOneMenu tag and it's works fine, but now I'm trying to implement it with autoComplete and I can't make it work properly because the method that is in the ajax tag returns null
this is my XHTML
<p:outputLabel value="Region"/>
<p:autoComplete value="#{beanPersona.region}" completeMethod="#{beanPersona.listarRegion}"
var="region" itemLabel="#{region.nombre}" itemValue="#{region}"
forceSelection="true" autocomplete="true" dropdown="true" >
<o:converter converterId="omnifaces.ListConverter" list="#{beanPersona.listRegion}"/>
<p:ajax event="itemSelect" listener="#{beanPersona.onRegionChange}" process="#form"/>
</p:autoComplete>
and this is my Bean (Scoope is ViewScope and everithing else works fine)
public void onRegionChange(SelectEvent e) {
System.out.println(e.getObject());
this.region = (Region) e.getObject();
}
public List<Region> listarRegion(String filtro) {
List<Region> listaCompleta = (List<Region>) (List<?>) new Dml().list(new Region());
List<Region> lista = new ArrayList();
if(filtro.length() > 0)
for (Region objeto : listaCompleta) {
if(objeto.getNombre().toLowerCase().contains(filtro.toLowerCase()))
lista.add(objeto);
}
else
lista = listaCompleta;
return lista;
}
if you could help me I would appreciate it

Primefaces how to toggle a dashboard widget/panel visibility using ajax?

I have a dashboard with a considerable number of widgets / panels which are working just fine.
I'm looking for a way to toggle the visibility of a specific one using a commandButton ction listener without having to refresh the page, i.e. via AJAX.
<p:dashboard id="board" model="#{dashboardBean.model}">
<!-- iteration code begins -->
<p:panel id="#{wdgt.code}" header="#{wdgt.title}">
<h:outputText value="One of the dozens of widgets" />
</p:panel>
<!-- iteration code ends -->
<p:panel id="staticWdgtId" header="Static Widget Initially Invisible" visible="false">
Some static content
</p:panel>
</p:dashboard>
Then in the backing bean, at some point this action needs to be fired via a commandButton or an actionListener...
public void showTheWidget() {
DashboardColumn dbc = this.model.getColumn(1);
dbc.getWidget(2); // does not get a widget object with a visibility attribute :((
// so that I could manipulate such as dbc.getWidget(2).setVisible(true);
}
Any ideas?
STATIC APPROACH
You can associate the panel with a boolean.
Panel
<p:panel id="staticWdgtId" header="Static Widget Initially Invisible"
visible="#{bean.panelShow}">
Some static content
</p:panel>
Button
<p:commandButton actionListener="#{bean.actionListener()}"
value="Button"
update=":staticWdgtId" />
Bean
public void actionListener() {
setShowPanel(true);
}
DYNAMIC APPROACH
Render all the panel with display: none
Dashboard
<p:dashboard id="board" model="#{dashboardBean.model}">
<p:panel id="#{wdgt.code}" header="#{wdgt.title}" style="display: none">
<h:outputText value="One of the dozens of widgets" />
</p:panel>
</p:dashboard>
remoteCommand
<p:remoteCommand name="panelsToShow"
actionListener="#{bean.panelsToShowAction()}"
oncomplete="handleComplete(xhr, status, args)" />
bean.panelsToShowAction() you need Gson
public void panelsToShowAction() {
List<String> panels = new ArrayList<String>();
//iterate over the panels you want to show, and put #{wdgt.code} which is the id of the panel
panels.add("Code1");//id
panels.add("Code2");//id
RequestContext requestContext = RequestContext.getCurrentInstance();
requestContext.addCallbackParam("panels", new Gson().toJson(panels));
}
JS
$(document).ready(function() {
panelsToShow();
})
function handleComplete(xhr, status, args) {
var panels = eval('(' + args.panels + ')');
for (var i = 0, len = panels.length; i < len; i++) {
$('#'+panels[i]).show();
}
}

How to use p:graphicImage with StreamedContent within p:dataTable? [duplicate]

This question already has answers here:
Display dynamic image from database or remote source with p:graphicImage and StreamedContent
(4 answers)
Closed 7 years ago.
I want to dynamically load images from a database withing a PrimeFaces data table. Code looks like as follows which is based on this PF forum topic:
<p:dataTable id="tablaInventario" var="inv" value="#{registrarPedidoController.inventarioList}" paginator="true" rows="10"
selection="#{registrarPedidoController.inventarioSelected}" selectionMode="single"
update="tablaInventario tablaDetalle total totalDesc" dblClickSelect="false" paginatorPosition="bottom">
<p:column sortBy="producto.codigo" filterBy="producto.codigo">
<f:facet name="header">#{msg.codigo}</f:facet>
#{inv.producto.codProducto}
</p:column>
<p:column>
<f:facet name="header">Foto</f:facet>
<p:graphicImage id="photo" value="#{registrarPedidoController.streamedImageById}" cache="FALSE">
<f:param name="inv" value="#{inv.id}" />
</p:graphicImage>
</p:column>
</p:dataTable>
with
public StreamedContent getStreamedImageById() {
DefaultStreamedContent image = null;
String get = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("inv");
System.out.println("[Param]: " + get); // This prints null.
Long id = new Long(get);
List<Inventario> listInventarios = controladorRegistrarPedido.listInventarios();
for (Inventario i : listInventarios) {
if (i.getId().compareTo(id) == 0) {
byte[] foto = i.getProducto().getFoto();
image = new DefaultStreamedContent(new ByteArrayInputStream(foto), "image/png");
}
}
return image;
}
However I can't get it work. My param is passing "null" to my backing bean. How is this caused and how can I solve it?
I am using Netbeans 6.9.1, JSF 2.0 and Primefaces 2.2.RC2.
I went on using BalusC first solution, it worked fine but images aren't being rendered in the UI. Exceptions Glassfish is throwing up:
WARNING: StandardWrapperValve[Faces Servlet]: PWC1406: Servlet.service() for servlet Faces Servlet threw exception
java.lang.NullPointerException
at com.sun.faces.mgbean.BeanManager$ScopeManager$ViewScopeHandler.isInScope(BeanManager.java:552)
Well seems I get working thanks to BalusC. I've to used RequestScoped, SessionScoped or ApplicationScoped for managing the getStreamedImageId. However in the UI is always setting the default image (for the null cases) and not as expected the image that correspondes to each row. The new code is:
public StreamedContent streamedById(Long id) {
DefaultStreamedContent image = null;
System.out.println("[ID inventario]: " + id);
List<Inventario> listInventarios = controladorRegistrarPedido.listInventarios();
for (Inventario i : listInventarios) {
if (i.getId().equals(id)) {
byte[] foto = i.getProducto().getFoto();
if (foto != null) {
System.out.println(" [Foto]: " + foto);
image = new DefaultStreamedContent(new ByteArrayInputStream(foto), "image/png");
break;
}
}
}
if (image == null) {
System.out.println(" [Image null]");
byte[] foto = listInventarios.get(0).getProducto().getFoto();
image = new DefaultStreamedContent(new ByteArrayInputStream(foto), "image/png");
}
System.out.println(" [Foto Streamed]: " + image);
return image;
}
The <p:graphicImage> will call the getter method twice. First time is when the <img> element is to be rendered to HTML and thus requires an URL in the src attribute. If you just return new DefaultStreamedContent(), then it will autogenerate the right URL in src attribute. Second time is when the browser really requests the image, this is the moment when you should return the actual image.
So, the getter method should basically look like this:
public StreamedContent getStreamedImageById() {
FacesContext context = FacesContext.getCurrentInstance();
if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
// So, we're rendering the view. Return a stub StreamedContent so that it will generate right URL.
return new DefaultStreamedContent();
}
else {
// So, browser is requesting the image. Get ID value from actual request param.
String id = context.getExternalContext().getRequestParameterMap().get("id");
Image image = service.find(Long.valueOf(id));
return new DefaultStreamedContent(new ByteArrayInputStream(image.getBytes()));
}
}
images are saved in the database as byte[] if we save them through hibernate. I uploaded the images with <p:fileUpload... tag then I save the image alongwith other data values using hibernate.
On second page, I'm displaying the whole table data (of course with images) using
<p:dataTable var="data" value="#{three.all}" ....
and dynamic Images using
<p:graphicImage alt="image" value="#{three.getImage(data)}" cache="false" >
<f:param id="image_id" name="image_id" value="#{data.number}" />
</p:graphicImage></p:dataTable>
Here "three" is the name of Backing Bean. In method getAll(), I'm retrieving data from table through hibernate and in the same method, I've created a HashMap<Integer, byte[]>. HashMap is an instance variable of the bean and The Bean is SessionScoped. I'm putting the images (which are in byte[] form) alongwith an integer image_id.
code:
for (int i=0; i<utlst.size(); i++ ){
images.put(utlst.get(i).getNumber(), utlst.get(i).getImage());}
//utlst is the object retrieved from database. number is user-id.
In the view getImage.xhtml, <p:graphicImage alt="image" value="#{three.getImage(data)}" cache="false" > it calls the method getImage(data /*I am passing current object of the list which is being iterated by*/ )
code of getImage:
public StreamedContent getImage(Util ut) throws IOException {
//Util is the pojo
String image_id = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("image_id");
System.out.println("image_id: " + image_id);
if (image_id == null) {
defaultImage=new DefaultStreamedContent(FacesContext.getCurrentInstance().getExternalContext().getResourceAsStream("/Capture.PNG"), "image/png");
return defaultImage;
}
image= new DefaultStreamedContent(new ByteArrayInputStream(images.get(Integer.valueOf(image_id))), "image/png");
return image;
}
just keep your dynamic Images with ids in A HashMap in the session then they will be correctly streamed.
Thanks & regards,
Zeeshan
In PrimeFaces 3.2 the bug is still present. I do the workaround with
<p:graphicImage value="#{company.charting}">
<f:param id="a" name="a" value="#{cc.attrs.a}" />
<f:param id="b" name="b" value="#{cc.attrs.b}" />
</p:graphicImage>
and
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
String a= externalContext.getRequestParameterMap().get("a");
String b= externalContext.getRequestParameterMap().get("b");
But even with this the bean is called 2 times. But in the second call variable a + b is filled ;-)
Damn bug

Resources