I'm using PrimeFaces for a new project and it's quite an impressive set of components.
Anyway, I have problem with "real world" use of filedownload component.
In my page I have a datalist that shows the attachments related to a particular document, and I want provide a link to directly download that file inside the datalist item.
Here's my xhtml code:
<p:dataList id="ListaAllegati" value="#{documentBean.documento.allegati}" type="definition" var="attach" style="border: none" ">
<f:facet name="description">
<h:outputText value="#{attach.name}" />
<p:commandLink ajax="false" title="Download" action="#{documentBean.selectAttach}>
<h:graphicImage style="margin-left: 10px; border: none" value="./images/article.png" height="24" width="24" ></h:graphicImage>
<p:fileDownload value="#{documentBean.downloadFile}"/>
<f:setPropertyActionListener target="#{documentBean.selectedAttach}" value="#{attach}" />
</p:commandLink>
</f:facet>
</p:dataList>
and the relative java bean (request scoped):
private StreamedContent downloadFile;
public StreamedContent getDownloadFile() {
log.info("getter dell'allegato invocato");
InputStream stream = null;
byte[] rawFile = null;
if (selectedAttach == null) {
log.warn("Nessun allegato passato");
return null;
} else {
try {
log.info("Recupero del file " + selectedAttach.getGuid());
rawFile = attachManager.retrieveFile(selectedAttach.getGuid());
} catch (Exception e) {
String msg = "Errore durante il recupero del file";
log.error(msg, e);
FacesMessage fmsg = new FacesMessage(msg, "");
FacesContext.getCurrentInstance().addMessage(null, fmsg);
}
stream = new ByteArrayInputStream(rawFile);
DefaultStreamedContent file = new DefaultStreamedContent(stream,
selectedAttach.getMimeType(), selectedAttach.getName());
return file;
}
}
public void selectAttach() {
log.info("commandLink action invocata");
}
private Allegato selectedAttach;
public Allegato getSelectedAttach() {
return selectedAttach;
}
public void setSelectedAttach(Allegato selectedAttach) {
log.info("Allegato selezionato");
if (selectedAttach==null) log.warn("L'allegato passato è nullo");
this.selectedAttach = selectedAttach;
}
So, couple of question:
Am I doing the right thing trying to pass the selected attachment that way? Otherwise, how can I pass a parameter to tell the bean wich attachment has been clicked?
Why the first time I click the command link, nothing happen? It make a roundtrip with server, but nothing happens. Second time, it gives me an exception.
Why documentBean.selectAttach is never called and the documentBean.selectedAttach property is never set (neither the second time)?
Thanks to anyone for any hint
How to get the row object from the datatable is answered in this question:
How can I pass selected row to commandLink inside dataTable?
This answers basically all the three questions.
As to the exception in the second click, that's likely because you didn't return from the catch block when an exception is been thrown in your getDownloadFile() method. You're continuing the remnant of the code flow while the rawFile is still null. Fix it accordingly as well. Add a return null to the end of catch or something. Better yet, you should be posting the entire stacktrace in the question as you don't seem to be able to understand it. It basically already contains the answer :)
Primefaces has its own dedicated servlet for file download and upload components that handle all of this asynchronously.
Try doing something like what I have in my code
<p:commandLink ajax="false" actionListener="#{managedBean.downloadAction(object)}">
<span class="ui-icon icoFolderGo" style="padding-right: 1.5em;" />
<p:fileDownload value="#{managedBean.downloadContentProperty}" />
</p:commandLink>
And in the managed bean,
public void downloadAction(Object object) {
try {
InputStream stream = // get input stream from argument
this.setDownloadContentProperty(new DefaultStreamedContent(stream, "application/pdf", "filename.pdf");
} catch (Exception e) {
log.error(e);
}
}
public void setDownloadContentProperty(StreamedContent downloadContentProperty) {
this.downloadContentProperty = downloadContentProperty;
}
public StreamedContent getDownloadContentProperty() {
return downloadContentProperty;
}
Related
I was trying to display a panel where user can see a list of items category(displayed as images) and on clicking they can view products within the category(images will be displayed)
For displaying the item category, i used the ui:repeat nad the supporting bean calss
Below is my xhtml code
<ui:repeat id="repeat" value="#{getData.images}" var="img" varStatus="loop">
<h:panelGroup>
<p:graphicImage id="img1" value="#{img}" alt="image not available" >
</p:graphicImage>
</h:panelGroup>
</ui:repeat>
And the Managed Bean Code parts
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
private List<StreamedContent> imageList = new ArrayList<StreamedContent>();
public List<StreamedContent> getImages(){
for (int i = 0; i < sdh.getNumOfImages(); i++) {
imageID = imageIDArray.get(i);
ImageService imgSer = new ImageService();
imgList.add(imageID);
imgSer.setData(imageID);
baos = imgSer.getImage();
try {
imageList.add(new DefaultStreamedContent(new
ByteArrayInputStream(baos.toByteArray())));
} catch (Exception ex) {
ex.printStackTrace();
}
}
imageNum = 0;
return imageList;
}
public StreamedContent getData() {
baos = imageList.get(imageNum);
//imageList.add(baos);
imageNum++;
return new DefaultStreamedContent(new ByteArrayInputStream(baos.toByteArray()));
}
Now my problem if i don't uncomment the 'imageList.add(baos)' in 'getData', the images are not displayed.
Now i really wants to know how the 'ui:repeat' works, since the 'imageList' contains the images and i can save the same if required in either of the method. If i specify a fixed number (ex:'imageList.get(0)') in the 'getData' method then the same image is show multiple times. Where as if i put the 'imageNum' without the 'imageList.add(baos)' it throw error 'Error in streaming dynamic resource'
I tired Bjorn Pollex's suggestion and made the necessary changes but now images don't appear
It is not possible to use <p:graphicImage> this way. You should rather iterate over a collection of unique image identifiers, not over a collection of StreamedContent. Those unique image identifiers have then to be passed as a <f:param> to <p:graphicImage> which in turn will generate the right URLs for the browser.
<ui:repeat value="#{data.imageIds}" var="imageId">
<p:graphicImage value="#{imageStreamer.image}">
<f:param name="id" value="#{imageId}" />
</p:graphicImage>
</ui:repeat>
Your #{data} managed bean must just have a:
private List<Long> imageIds; // +getter
The #{imageStreamer} should be a separate application scoped managed bean which look basically like this:
#ManagedBean
#ApplicationScoped
public class ImageStreamer {
#EJB
private ImageService service;
public StreamedContent getImage() throws IOException {
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()));
}
}
}
You used wrong ui:repeat tag. You have var attribute but you can't use this in p:graphicImage tag value attribute.Please see sample usage,
<ui:repeat value="#{yourBean.images}" var="img">
<p:graphicImage value="/images/#{img}" />
</ui:repeat>
This question already has answers here:
How to use Primefaces' p:growl and redirect to a page
(3 answers)
Closed 6 years ago.
I'm using primefaces 3.5 and I can't figure it out how to growl a message on the next page. For instance I want to add a record in database and after that I make a redirection to another page where I want to show a growl message with "The record has been added with success!"
I tried something like this:
public String addLabelInDB() {
try {
//logic to add a record in DB
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Success!", "Label has been added with success!"));
} catch (Exception e) {
logger.debug(e.getMessage());
}
return "listLabelsPage";
}
and in listLabelsPage.xhtml I have:
<p:growl id="msgs" showDetail="true" autoUpdate="true"/>
but it doesn't work.
I supposed the message is getting lost because is another request or something? It's there any possibility to store the message on request and show it on the next page? Thanks!
You can have a preRender set on the listLabelsPage.xhtml page you're loading
<f:event type="preRenderView" listener="#{yourBean.showGrowl}" />
and a showGrowl method having only
public void showGrowl() {
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Success!", "Label has been added with success!"));
}
I post an answer to my own question in order to help another people which face the same problem like I did:
public String addLabelInDB() {
try {
//some logic to insert in db
//below I set a flag on context which helps me to display a growl message only when the insertion was done with success
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.getRequestMap().put("addedWithSuccess","true");
} catch (Exception e) {
logger.debug(e.getMessage());
}
return "listLabelsPage";
}
public void showGrowl() {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
String labelAddedWithSuccess = (String) ec.getRequestMap().get("addedWithSuccess");
//if the flag on context is true show the growl message
if (labelAddedWithSuccess!=null && labelAddedWithSuccess.equals("true")) {
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Success!", "Label has been added with success!"));
}
}
and in my xhtml I have:
<f:event type="preRenderView" listener="#{labelsManager.showGrowl}" />
How about this? Make a separated redirect button which will be hit after showing msg:
HTML:
<h:form prependId="false">
<p:growl />
<p:button outcome="gotoABC" id="rdr-btn" style="display: none;" />
<p:commandButton action="#{bean.process()}" update="#form" />
</form>
Bean:
public void process(){
addInfoMsg(summary, msgDetail); //Add msg func
RequestContext.getCurrentInstance().execute("setTimeout(function(){ $('#rdr-btn').click(); }, 3000);"); // 3 seconds delay. I put the script in Constants to config later.
}
I'm trying to select actions from a list of given ones. It's a List<String>, so I don't use a converter. I don't know, why there is no assignment is taking place. Whatever I shift from one side to the other is only visible in the web, but the targetValue stays empty. Why? Do you see anything?
Here is a part of the .xhtml file (wich is ui:included in an other xhtml file, where a h:form element is wrapped around):
<h:outputText style="font-weight:bold" value="#{labels['maint.works']}" />
<r:listShuttle sourceValue="#{orderEdit.unselectedActionList}" var="_act"
targetValue="#{orderEdit.selectedActionList}" style="margin:0px 0px 0px -5px; font-weight:normal"
sourceCaptionLabel="#{labels['maint.works.list']}" targetCaptionLabel="#{labels['maint.works.choice']}"
showButtonLabels="false" orderControlsVisible="false" fastOrderControlsVisible="false"
listsHeight="150" sourceListWidth="170" targetListWidth="170" >
<r:column>
<h:outputText value="#{_act}" />
</r:column>
</r:listShuttle>
Here is my Bean:
// list of "Ausgeführte Arbeiten"
List<TblUserAction> actionListDb = dataStore.getActionByMatchCode(userInfo.getUserName());
for(TblUserAction action : actionListDb) {
unselectedActionList.add(action.getAction());
}
and the getters and setters in that Bean:
public List<String> getSelectedActionList() {
return selectedActionList;
}
public void setSelectedActionList(List<String> selectedActionList) {
this.selectedActionList = selectedActionList;
public List<String> getUnselectedActionList() {
return unselectedActionList;
}
public void setUnselectedActionList(List<String> unselectedActionList) {
this.unselectedActionList = unselectedActionList;
}
Any help is appreciated! Thank you.
Well, you aren't sending any data to the server, so the server doesn't know there have been any changes.
You can use a submit button:
<a4j:commanButton process="listShuttleId" value="Submit">
or you can have the changes sent to server as they're happening by putting this inside the listShuttle:
<a4j:support event="onlistchanged" />
I was trying to display a panel where user can see a list of items category(displayed as images) and on clicking they can view products within the category(images will be displayed)
For displaying the item category, i used the ui:repeat nad the supporting bean calss
Below is my xhtml code
<ui:repeat id="repeat" value="#{getData.images}" var="img" varStatus="loop">
<h:panelGroup>
<p:graphicImage id="img1" value="#{img}" alt="image not available" >
</p:graphicImage>
</h:panelGroup>
</ui:repeat>
And the Managed Bean Code parts
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
private List<StreamedContent> imageList = new ArrayList<StreamedContent>();
public List<StreamedContent> getImages(){
for (int i = 0; i < sdh.getNumOfImages(); i++) {
imageID = imageIDArray.get(i);
ImageService imgSer = new ImageService();
imgList.add(imageID);
imgSer.setData(imageID);
baos = imgSer.getImage();
try {
imageList.add(new DefaultStreamedContent(new
ByteArrayInputStream(baos.toByteArray())));
} catch (Exception ex) {
ex.printStackTrace();
}
}
imageNum = 0;
return imageList;
}
public StreamedContent getData() {
baos = imageList.get(imageNum);
//imageList.add(baos);
imageNum++;
return new DefaultStreamedContent(new ByteArrayInputStream(baos.toByteArray()));
}
Now my problem if i don't uncomment the 'imageList.add(baos)' in 'getData', the images are not displayed.
Now i really wants to know how the 'ui:repeat' works, since the 'imageList' contains the images and i can save the same if required in either of the method. If i specify a fixed number (ex:'imageList.get(0)') in the 'getData' method then the same image is show multiple times. Where as if i put the 'imageNum' without the 'imageList.add(baos)' it throw error 'Error in streaming dynamic resource'
I tired Bjorn Pollex's suggestion and made the necessary changes but now images don't appear
It is not possible to use <p:graphicImage> this way. You should rather iterate over a collection of unique image identifiers, not over a collection of StreamedContent. Those unique image identifiers have then to be passed as a <f:param> to <p:graphicImage> which in turn will generate the right URLs for the browser.
<ui:repeat value="#{data.imageIds}" var="imageId">
<p:graphicImage value="#{imageStreamer.image}">
<f:param name="id" value="#{imageId}" />
</p:graphicImage>
</ui:repeat>
Your #{data} managed bean must just have a:
private List<Long> imageIds; // +getter
The #{imageStreamer} should be a separate application scoped managed bean which look basically like this:
#ManagedBean
#ApplicationScoped
public class ImageStreamer {
#EJB
private ImageService service;
public StreamedContent getImage() throws IOException {
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()));
}
}
}
You used wrong ui:repeat tag. You have var attribute but you can't use this in p:graphicImage tag value attribute.Please see sample usage,
<ui:repeat value="#{yourBean.images}" var="img">
<p:graphicImage value="/images/#{img}" />
</ui:repeat>
Well currently I have this:
<rich:fileUpload addLabel="Agregar" clearAllLabel="Quitar todos"
clearLabel="Quitar" deleteLabel="Quitar"
doneLabel="Completado" uploadLabel="Subir archivos"
fileUploadListener="#{uploadBean.doUpload}"
acceptedTypes="txt, csv"
noDuplicate="true">
<a4j:ajax event="uploadcomplete" render="validationButton"/>
<a4j:ajax event="clear" listener="#{uploadBean.doClearFilesList}"
render="validationButton"/>
</rich:fileUpload>
On the backing bean I have a list of the files uploaded. When I click on Clear/Clear all button the event clear is fired and the method doClearFilesList (which just clears the list of files uploaded) is perfectly when the user hits the Clear All button, but If the user clicks on Clear button It should just delete the item on the list corresponding to the file cleared.
What can I do on my UploadBean.doClearFilesList method to delete a single file from the list? Should be something like:
public void doClearFilesList(){
files.clear(); //when CLEAR ALL is clicked
files.remove(oneFile); //when CLEAR is clicked
validationButtonRendered = false;
}
Any idea?
Cheers
UPDATE
RichFaces 4.1.0 Final
JSF Mojarra 2.1.6
Tomcat 7
I am not clear at which point you failed to run the sample described at https://community.jboss.org/message/727544#727544
However I hope following would work for you which is very similar to above sample.
Page:
<h:head>
<script>
function clear(event) {
var files = new Array();
var data = event.rf.data;
for (var i in data) {
files[i] = data[i].name;
}
clearFunc(files);
}
</script>
</h:head>
<body>
<h:form>
<rich:fileUpload onclear="clear(event);"/>
<a4j:jsFunction name="clearFunc" action="#{del.clearFile}" ajaxSingle="true">
<a4j:param name="fName" assignTo="#{del.fileNames}" />
</a4j:jsFunction>
</h:form>
</body>
Class:
public class Del {
String[] fileNames;
public void clearFile() {
for(String name : fileNames) {
System.out.println(">>" + name);
//Do file removing part here
}
}
public String[] getFileNames() {
return fileNames;
}
public void setFileNames(String[] fileNames) {
this.fileNames = fileNames;
}
}
Add "onclear" attribute to your <rich:fileUpload/> component and call a <a4j:jsFunction/> and pass the file name to it as below.
<rich:fileUpload onclear="clearFunc(event.memo.entry.fileName);" ..../>
Your <a4j:jsFunction/> should be as below.
<a4j:jsFunction name="clearFunc" actionListener="#{uploadBean.clearFile}" ajaxSingle="true">
<a4j:actionparam name="fName" />
</a4j:jsFunction>
Inside the listener method you can access the file name as below.
public void clearFile(ActionEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
String fileName = context.getExternalContext().getRequestParameterMap().get("fName").toString();
System.out.println("fileName = " + fileName);}