RichFaces 4 fileupload clear and clear all buttons - jsf

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);}

Related

How to get selected files from p:fileUpload after selection and before uploading

I have an application with a file uploader and I would like to display some information from the selected files before the user uploads them.
E.g. The user selects a file to upload, the application, client side, then grabs that file and reads some information from it to display to the view. Then if it is what the user expects to see they can hit upload.
Is there a way to call a method in the backing bean when a file is selected and pass it that file, or does PrimeFaces not let this happen?
index.xhtml
<h:form id="uploadform" prependId="false" enctype="multipart/form-data">
<p:outputPanel id="container">
<center>
<p:fileUpload fileUploadListener="#{uploadBean.handleFileUpload}" mode="advanced"
dragDropSupport="false" allowTypes="/(\.|\/)(csv|xlsx)$/" update="messages"/>
<p:growl id="messages" showDetail="true" />
</center>
</p:outputPanel>
</h:form>
UploadBean.java
import org.apache.poi.util.IOUtils;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.UploadedFile;
#ViewScoped
#ManagedBean(name = "uploadBean")
public class NetezzaUploadBean implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private UploadedFile file = null;
#PostConstruct
public void init() {
}
public void getFileBeforeSubmit() {
//Where I want to do some work with the file
}
public void handleFileUpload(FileUploadEvent event){
FacesMessage message = new FacesMessage("Succesful", event.getFile().getFileName() + " is uploaded.");
FacesContext.getCurrentInstance().addMessage(null, message);
}
public UploadedFile getFile() {
return file;
}
public void setFile(UploadedFile uploadedFile) {
this.file = uploadedFile;
}
}
PrimeFaces p:fileUpload seems to have a great undocumented feature where you can make use of the native file input 'onAdd' event (or sort of). I found this in the source (which is open ;-)) of the 2-fileUpload.js file
if($this.cfg.onAdd) {
$this.cfg.onAdd.call($this, file, function(processedFile) {
file = processedFile;
data.files[0] = processedFile;
this.addFileToRow(file, data);
});
}
The cfg property from $this can be accessed via
PF('myWidgetId').cfg
And if you declare a function upfront like
function myOnAddHandler(file) {
console.log(file);
}
And add it to the widget with
PF('myWidgetId').cfg.myOnAddHandler;
You can select a file and before uploading see it logged in the console
File { name: "myImage.PNG", lastModified: 1533756086560, webkitRelativePath: "", size: 38344, type: "image/png" }
You can then extend this to use the HTML5 File API and read it
function myOnAddHandler(file) {
var reader = new FileReader();
reader.onload = function(readerEvt) {
var binaryString = readerEvt.target.result;
console.log(btoa(binaryString));
};
reader.readAsBinaryString(file);
}
PrimeFaces itself uses this sort of too in the related addFileToRow to show the preview
After looking in more of the java code of PrimeFaces, it might even be that instead of doing PF('myWidgetId').cfg.myOnAddHandler;, you could do <p:fileUpload onAdd="myOnAddHandler" .... /> I unfortunately do not have the time to test this right now but it might work.

Datatable-Pagination with <h:link> and Url-Parameters

i am looking for a plain-jsf-solution to handle bookmarkable, parameterbased datatable-pagination.
actually i am using an ajax-approach and a cookie to store the active page. when the user hits F5 or clicks a link in a datatable-row and then returns with "browser-back", i check the cookie to show the last active page.
<h:commandLink value="Next Page">
<f:ajax listener="#{bean.nextPage}" render="dataTable"/>
</h:commandLink>
#ViewScoped
public class PagerBean {
public void nextPage() {
this.resultList = Products.getNextProducts(getFirstResult(),getMaxResult());
addCookie("activePage", getActivePage());
}
}
#ViewScoped
public class ProductBean {
#ManagedProperty(value="#{pager}")
protected PagerBean pager;
#Postconstruct
public void init() {
if (isCookie("activePage"){
pager.setActivePage(getCookie("activePage"));
}
}
}
however, i am looking for a bookmarkable solution, so that we can produce links with specific url-parameters, which are also trackable by browser back/forward-button.
http://foo.com/products?page=1
http://foo.com/products?page=2
http://foo.com/products?page=3
<h:link outcome="/pages/markets/products">
<f:param name="page" value="#{bean.pager.activePage}"/>
</h:link>
#ViewScoped
public class ProductBean {
#ManagedProperty(value="#{pager}")
protected PagerBean pager;
#Postconstruct
public void init() {
final String page = Faces.getRequestParameter("page");
if (null != page){
//load next entries
}
}
}
my only problem is, that with this version, the ViewScoped ProductBean gets newly created on every pagination-action. i think, as the view is not changing, the bean should not be re-created. what is the right approach to get lucky?
found a non-primefaces-solution using h:commandLink and HTML5 History API.
on every pagination-action the current pagenumber is stored in the history. when user navigates, the pagenumber will be restored from history and ajax-submitted again.
<h:commandLink value="Next Page">
<f:ajax listener="#{bean.nextPage}" render="dataTable" onevent="pushState"/>
</h:commandLink>
<h:inputText id="current" value="#{bean.pager.activePage}"/>
<h:commandLink value="Previous Page">
<f:ajax listener="#{bean.prevPage}" render="dataTable" onevent="pushState"/>
</h:commandLink>
<!--hidden action fired when user navigates in history-->
<h:commandLink styleClass="hidden" id="hiddenLink">
<f:ajax execute="current" listener="#{bean.jumpToPage}" render="dataTable" />
</h:commandLink>
JS:
$(window).on('popstate', function(event) {
var pageInHistory = event.originalEvent.state;
if (null == pageInHistory){
pageInHistory = 1;
}
//set page number from history
$('#current').val(pageInHistory);
//trigger ajax-submit
$('#hiddenLink').trigger('click');
});
pushState = function (data){
switch (data.status) {
case "success": {
var currentPage = $('#current').val();
history.pushState(currentPage, null, "?page=" + currentPage);
}
}
Bean
#ViewScoped
public class PagerBean {
private int activePage;
public void jumpToPage() {
//load data for activePage
}
//...
}

Disable "Choose" button fileUpload in primefaces

I have the fileLimit seted to 1.
I want that when the user choose the first file, the choose button get disabled.
I resolved putting a disabled atribut in fileUpload liked with a boolean variable in my bean.
When the file finish to upload, the variable change his values to true and I update the fileUpload componnt, then the component get disabled.
Thank you guys to the help.
Try this.
xhtml
<p:fileUpload fileUploadListener="#{fileUploadView.handleFileUpload}"
mode="advanced"
dragDropSupport="false"
update="messages,#this"
sizeLimit="100000"
fileLimit="1"
allowTypes="/(\.|\/)(gif|jpe?g|png)$/"
disabled="#{fileUploadView.fileUploadCount >= 1}"/>
<p:growl id="messages" showDetail="true" />
managedbean
#ManagedBean
public class FileUploadView {
private int fileUploadCount;
public int getFileUploadCount() {
return fileUploadCount;
}
public void setFileUploadCount(int fileUploadCount) {
this.fileUploadCount = fileUploadCount;
}
public void handleFileUpload(FileUploadEvent event) {
FacesMessage msg = new FacesMessage("Succesful", event.getFile().getFileName() + " is uploaded.");
FacesContext.getCurrentInstance().addMessage(null, msg);
fileUploadCount = fileUploadCount + 1;
}
}
This proposed is easy to change file limit. When you want to change fileLimit to 2 you just change
fileLimit="2" and disabled="#{fileUploadView.fileUploadCount >= 2}" without modify managedbean.

Input fields hold previous values only if validation failed

I came up with a strange problem. I tried to isolate the problem so following is my simplified code.
public class MyBean {
private List<Data> dataList;
Data selectedData;
public MyBean() {
dataList = new ArrayList<Data>();
dataList.add(new Data("John", 16));
dataList.add(new Data("William", 25));
}
public List<Data> getDataList() {
return dataList;
}
public void edit(Data data) {
selectedData = data;
}
public void newData() {
selectedData = new Data(null, null);
}
public Data getSelectedData() {
return selectedData;
}
public class Data {
String name;
Integer age;
Data(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
}
xhtml:
<rich:modalPanel id="pop">
<h:form>
Name: <h:inputText value="#{myBean.selectedData.name}" required="true" id="txtName"/><br/>
Age : <h:inputText value="#{myBean.selectedData.age}" required="true" id="txtAge"/>
<a4j:commandButton value="Save"/>
<a4j:commandButton value="Close" onclick="Richfaces.hideModalPanel('pop');return false;"/>
<br/>
<rich:message for="txtName"/><br/>
<rich:message for="txtAge"/>
</h:form>
</rich:modalPanel>
<h:form>
<rich:dataTable value="#{myBean.dataList}" var="data">
<rich:column>#{data.name}</rich:column>
<rich:column>
<a4j:commandLink value="Edit" action="#{myBean.edit(data)}" reRender="pop" oncomplete="Richfaces.showModalPanel('pop')"/>
</rich:column>
</rich:dataTable>
<a4j:commandButton value="New" action="#{myBean.newData()}" reRender="pop" oncomplete="Richfaces.showModalPanel('pop')"/>
</h:form>
This is the path to error:
Load the page
Click the "Edit" link in first row(popup displays)
In popup, clear the "Age" field and click "Save".(Required message shown)
Click cancel(without filling "Age" field)
Click second link.
Now it shows irrelevant data(previous data). - This is the problem
Even when I click "New" button it shows incorrect data.
This happens only if a validation is failed in the popup.
Is there a solution for this?
This problem is in JSF 2 also recognized and explained in detail in the following answer: How can I populate a text field using PrimeFaces AJAX after validation errors occur? If you were using JSF 2, you could have used OmniFaces' ResetInputAjaxActionListener or PrimeFaces' <p:resetInput> or resetValues="true" for this.
To the point, you need to clear the state of the EditableValueHolder component when it's about to be ajax-rendered, but which isn't included in the ajax-execute. To clear the state, in JSF 2 you would have used the convenience method resetValue() for this, but this isn't available in JSF 1.2 and you need to invoke the 4 individual methods setValue(null), setSubmittedValue(null), setLocalValueSet(false), setValid(true) to clear the state.
To figure out which components are to be ajax-rendered, but aren't been ajax-executed, in JSF 2 you would have used PartialViewContext methods for this, but this is not available in JSF 1.2 which hasn't standardized ajax yet. You'd need to fiddle with RichFaces specific ajax API in order to figure that. I can't tell that from top of head, so here's a kickoff example assuming that you already know the components which needs to be cleared. Imagine that the form in your popup has id="popForm" and the name input field has id="nameInput", here's how you could clear it inside the newData() method:
UIInput nameInput = (UIInput) context.getViewRoot().findComponent("popForm:nameInput");
nameInput.setValue(null);
nameInput.setSubmittedValue(null);
nameInput.setLocalValueSet(false);
nameInput.setValid(true);
do one thing on cancel action set all popup values null. now in your next click all values set to be default.
or on click set all previous values null. and set all respective values after that.
I had the same problem. if you are using Primefaces, the solution is as simple as putting resetValues="true" on your p:commandLink or p:commandButton that loads the selected item.
After validation failed if you want to remain same as input data which you have pass as submission parameter, then set value attribute as your form bean name as mention below i.e.
<input type="text" id="fname" path="fname" value="${myFormBean.fname}"/>

JSF PrimeFaces FileDownload problem

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;
}

Resources