How to display p:fileUpload invalidFileMessage in p:growl - jsf

I'm using <p:fileUpload> which is restricted to PDF only. However, the invalidFileMessage shows inside the <p:fileUpload> component. How can I show it in <p:growl> instead?
<p:fileUpload allowTypes="/(\.|\/)(pdf)$/"
invalidFileMessage="File is Invalid. Only PDF files are allowed" />

You can't handle this server side. The file type is validated at client side without hitting any code in server side. So, any suggestions which suggest to manually create FacesMessage and/or explicitly add <p:message(s)> are unthoughtful and untested.
You should use jQuery. It solves everything.
Based on the fileupload.js source code, your best bet is to listen on the fictional show event of the message container and then move the messages container to end of the form.
First extend $.show() to actually trigger the show event.
(function($) {
var originalShowFunction = $.fn.show;
$.fn.show = function() {
this.trigger("show");
return originalShowFunction.apply(this, arguments);
};
})(jQuery);
Then simply create a listener on show event which basically runs when file upload messages appear and then parse every single message and use the renderMessage() function of the <p:growl> JS API. The below example assumes that you've a <p:growl widgetVar="growl"> somewhere in the same page.
$(document).on("show", ".ui-fileupload-content>.ui-messages", function() {
$(this).children().hide().find("li").each(function(index, message) {
var $message = $(message);
PF("growl").renderMessage({
summary: $(".ui-messages-error-summary", $message).text(),
detail: $(".ui-messages-error-detail", $message).text()
});
});
});

Well add an message tag in your page something like:
<p:messages id="test" autoUpdate="true" />
And in fileupload update="#this,test" and your message will be displayed in p:messages. You can change easly in growl works the same.
Look in the primefaces showcase for more examples

Looked up an example in Primefaces showcase and found this. The actual page:
<p:fileUpload fileUploadListener="#{fileUploadController.handleFileUpload}"
mode="advanced"
update="messages"
allowTypes="/(\.|\/)(pdf)$/"/>
<p:growl id="messages" showDetail="true"/>
And the file uploader controller class:
public void handleFileUpload(FileUploadEvent event) {
FacesMessage msg = new FacesMessage("Succesful", event.getFile().getFileName() + " is uploaded.");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
Maybe something to keep in mind on how to display messages in Primefaces

Related

Show error and change doneLabel of rich:fileUpload in upload listener method

I have to upload CSV file and I have to validate some values of file, for example does not allow negative value. Then, I need validate it and that file does not apear like "done" or "uploaded". I need handle an error on display it.
<rich:fileUpload
fileUploadListener="#{configClientBean.listener}"
ontyperejected="alert('some error');"
maxFilesQuantity="1"
onuploadcomplete="#{rich:component('waitPanelInterno')}.hide();"
onfilesubmit="#{rich:component('waitPanelInterno')}.show();"
render="pnlMensajes, idTableClients, scrollRegistros, outputPanelDetalle"
autoclear="true">
<rich:message for="uploadConfigClient" />
<a4j:ajax event="uploadcomplete" execute="popupFileLoad"
render="panelCarga, pnlMensajes" />
</rich:fileUpload>
In the Backing bean I can validate some things, but I cant show errors or change the behavior of "rich:fileUpload" compoment for example doneLable can not be displayed.
public void listener(FileUploadEvent event) throws Exception {
try {
UploadedFile file = event.getUploadedFile();
ByteArrayInputStream bais = new ByteArrayInputStream(file.getData());
InputStreamReader is = new InputStreamReader(bais, getMessage("iso"));
BufferedReader bufRead = new BufferedReader(is);
while ((registro = bufRead.readLine()) != null) {
if(cvsLine[1].isEmpty()){
// stop process
// Throw error
}
}
}
Thanks for your time.
To add extra behavior to the rich:fileUpload component that is not default is by creating your own file upload component (ex. myown:fileUpload) based on the rich:fileUpload source code and the use of the Component Development Kit.
A second solution could be by adding one extra message field that is described in this post: RichFaces fileupload and h:message problem

Update doesn't work when call sendRedirect (HttpServletResponse) [duplicate]

I have a button which opens a new tab with a generated pdf-file.
However, after I click on the button, I want to navigate to another page.
That means, after clicking on the button i want to open a new tab with the pdf and navigate to another page on the initial tab. I am using primefaces p:commandButton and tried with onclick="window.location.href='www.google.de'" but it does not work. However onclick="window.lalert('www.google.de')" does work.
This is my code:
<h:form id="transForm" target="_blank">
<p:commandButton value="Zertifikat erstellen" ajax="false"
label="Speichert die Anmeldung und erstellt ein Zertifikat im PDF-Format"
action="#{transportErfassen.generatePDFZertifikat()}"/>
</h:form>
generatePDFZertifikat() does create a pdf-File with following code, I think here is the issue:
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
externalContext.setResponseContentType("application/pdf" );
externalContext.setResponseHeader("Expires", "0");
externalContext.setResponseHeader("Cache-Control","must-revalidate, post-check=0, pre-check=0");
externalContext.setResponseHeader("Pragma", "public");
externalContext.setResponseHeader("Content-disposition", "inline; filename=\"" + fileName +"\"");
externalContext.setResponseContentLength(out.length);
externalContext.addResponseCookie(Constants.DOWNLOAD_COOKIE, "true", new HashMap<String, Object>());
//setze explizit auf OK
externalContext.setResponseStatus(200);
OutputStream os = externalContext.getResponseOutputStream();
os.write(out, 0, out.length);
os.flush();
facesContext.responseComplete();
facesContext.renderResponse();
You're basically trying to send 2 responses back to 1 request. This is not ever going to work in HTTP. If you want to send 2 responses back, you've got to let the client fire 2 requests somehow. You were already looking in the right direction for the solution, with little help of JavaScript it's possible to fire multiple requests on a single event (click). Your attempt in onclick is however not valid, the change of window.location on click of the submit button, right before the form submit, completely aborts the original action of the button, submitting the form.
Your best bet is to directly navigate to the result page which in turn invokes JavaScript window.open() on page load, pointing to the URL of the PDF file which you'd like to open. It's namely not possible to send some HTML/JS code along with the PDF file instructing a navigation (as that would obviously corrupt the PDF file). This also means, that you can't return the PDF directly to the form submit request. The code has to be redesigned in such way that the PDF can be retrieved by a subsequent GET request. The best way is to use a simple servlet. You could store the generated PDF temporarily on disk or in session, associated with an unique key, and pass that unique key as request pathinfo or parameter to the servlet in window.open() URL.
Here's a kickoff example:
Initial form:
<h:form>
...
<p:commandButton ... action="#{bean.submit}" />
</h:form>
Bean:
public String submit() {
File file = File.createTempFile("zertifikat", ".pdf", "/path/to/pdfs");
this.filename = file.getName();
// Write content to it.
return "targetview";
}
Target view:
<h:outputScript rendered="#{not empty bean.filename}">
window.open('#{request.contextPath}/pdfservlet/#{bean.filename}');
</h:outputScript>
PDF servlet (nullchecks etc omitted for brevity; Java 7 assumed for Files#copy()):
#WebServlet("/pdfservlet/*")
public class PdfServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
File file = new File("/path/to/pdfs", request.getPathInfo().substring(1));
response.setHeader("Content-Type", "application/pdf");
response.setHeader("Content-Length", String.valueOf(file.length()));
response.setHeader("Content-Disposition", "inline; filename=\"zertifikat.pdf\"");
Files.copy(file.toPath(), response.getOutputStream());
}
}
As BalusC said, Refresh/navigate current page and opening downloading file are two different responses, there must be two resquests. I encountered a similar problem. I solved it with jsf ajax successfully.
Here's part of my code:
XHTML:
<h:commandButton id="download-button" class="download-button"
value="download">
<f:ajax event="click" execute="#form" render=":msg-area"
listener="#{myController.checkForDownload}" onevent="checkCallBack" />
</h:commandButton>
<h:commandButton id="download-button2" class="download-button2"
value="download" style="display: none;"
action="#{myController.download}">
</h:commandButton>
Javascript:
function checkCallBack(data) {
var ajaxStatus = data.status;
switch (ajaxStatus) {
case "begin":
break;
case "complete":
break;
case "success":
document.getElementById('download-form:download-button2').click();
break;
}
}
download-button renders a message area on page and download-button2 triggers a download method. they are two different requests. When the first request completed, the second request will be triggered.

p:remoteCommand returns <eval> twice in ajax response

I try to render a new page in a new window (or tab) with the link I get from a selected page object in an autoComplete component.
After trying multiple options the only chance in my opinion is to use javascript to catch the submit, trigger a remote command, that gives me a javascript call with the link attribute from the page object.
JSF-Snipplet (with reduced attributes in autoComplete)
<h:form id="autoCompleteForm">
<p:autoComplete id="autoComplete" widgetVar="autoCompleteWidget" value="#{pageBean.selectedPage}" />
<p:remoteCommand action="#{pageBean.showPage}" name="showPage" />
</h:form>
some JS:
// form submit
('#autoCompleteForm').on('submit', function(e) {
e.preventDefault();
showPage();
});
// open link
var openLink = function(pageLink) {
window.open(pageLink, '_blank');
};
Bean part for action
public void showPage() {
RequestContext context = RequestContext.getCurrentInstance();
context.execute("openLink('" + selectedPage.getLink() + ".xhtml')");
}
Everything works nice together, but the response contains the eval tag twice.
<partial-response>
<changes>
<update id="javax.faces.ViewState"><![CDATA[2851816213645347690:-2276123702509360418]]></update>
<eval><![CDATA[openLink('target.xhtml');]]></eval>
<eval><![CDATA[openLink('target.xhtml');]]></eval>
</changes>
</partial-response>
I tried different approaches with redirects or returning view names, but that all leads to no satisfying solutions (e.g. URL not changing or no new window).
Problem solved:
I had defined PrimePartialViewContextFactoryin my faces-config before:
<factory>
<partial-view-context-factory>org.primefaces.context.PrimePartialViewContextFactory</partial-view-context-factory>
</factory>
By removing it the application acts like expected.
This also solves a problem (JSON.parse: unexpected non-whitespace character after JSON data) with pagination and sorting in DataTables.

Programmatically control which components should be ajax-updated

I have a complex form where the user fills a few fields, and has two options: generate a license file or save the changes. If the user clicks on the generate license file button without saving the changes, I render a small component with an error message asking him to save before generating the license.
To display the component with a warning message, I want to use ajax to avoid rendering the whole page just to render the warning component. Of course, if the changes were saved, then the warning message is not required and I redirect the user to another page.
I have a change listener on the changeable fields to detect when a change has been made. What I don't know is the conditional execution. The "render with ajax if unsaved OR redirect if saved" part. Here's the logic
if(saved){
redirect();
}else{
ajax.renderWarning()
}
--EDIT--
I'm going to add more info because I realized I'm leaving things too open ended.
Here's one example of an updateable field.
<h:inputText name="computername3" value="#{agreement.licenseServerBeans[2].computerId}" valueChangeListener="#{agreement.fieldChange}">
<rich:placeholder value="Add Computer ID"/>
</h:inputText>
The fieldChange() bean method
public void fieldChange(ValueChangeEvent event) {
change = true; //change is a boolean, obviously :P
}
Here's the generate license button jsf
<h:commandLink action="#{agreement.generateLicenseFile}">
<span class="pnx-btn-txt">
<h:outputText value="Generate License File" escape="false" />
</span>
</h:commandLink>
Here's the generateLicenseFile() method
public String generateLicenseFile(){
....//lots of logic stuff
return "/licenseGenerated.xhtml?faces-redirect=true";
}
Use PartialViewContext#getRenderIds() to get a mutable collection of client IDs which should be updated on the current ajax request (it's exactly the same as you'd specify in <f:ajax render>, but then in form of absolute client IDs without the : prefix):
if (saved) {
return "/licenseGenerated.xhtml?faces-redirect=true";
}
else {
FacesContext.getCurrentInstance().getPartialViewContext().getRenderIds().add("formId:messageId");
return null;
}
Returning null causes it to redisplay the same view. You can even add it as a global faces message and let the ajax command reference the <h:messages> in the render.
if (saved) {
return "/licenseGenerated.xhtml?faces-redirect=true";
}
else {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(...));
return null;
}
with
<h:messages id="messages" globalOnly="true" />
...
<f:ajax render="messages" />

Primefaces upload, how to only allow one upload in advance mode

I am wondering if it possible, by using the primefaces upload in advance mode to limit the user uploading one file only, currently i have :
<p:fileUpload fileUploadListener="#{fileUploadController.handleFileUpload}"
mode="advanced"
multiple="false"
update="messages"
sizeLimit="100000000"
allowTypes="/(\.|\/)(gif|jpe?g|png|doc|docx|txt|pdf)$/"
auto="false"/>
<p:growl id="messages" showDetail="true"/>
as you can see i have muliple ="false" but a user is still able to upload multiple files, any tips ?
EDIT :
<p:fileUpload widgetVar="upload" fileUploadListener="#{fileUploadController.handleFileUpload}"
mode="advanced"
multiple="false"
update="messages"
label="Select File"
sizeLimit="100000000"
allowTypes="/(\.|\/)(gif|jpe?g|png|doc|docx|txt|pdf|html)$/"
auto="false"/>
<p:growl id="messages" showDetail="true"/>
have added the widgetVar above
and in my js
<script type="text/javascript">
function Naviagtion()
{
//alert("Sent to the printing holding queue, you may close this app now, your work will still print out ");
window.setTimeout(afterDelay, 500);
location.href = 'FilesUploaded.xhtml';
}
upload.buttonBar.find('input[type=file]').change(function() {
if (this.value) {
var files = upload.uploadContent.find('.files tr');
if (files.length > 1) {
files.get(0).remove();
}
}
});
</script>
but i am still able to multi upload, am i going about this in the right direction
Although better behavior to solve it should be as #BalusC suggested, but in primefaces 4.0 I am seeing the attribute
fileLimit="1"
which you can set to 1 to disallow multiple file additions using "Choose" button. When user adds more file then it simply says
"Maximum number of files exceeded"
The multiple="false" only tells the webbrowser to disable multiple file selection in the browser-specific Browse dialog. However, it indeed doesn't prevent the enduser from clicking multiple times on the Choose button of the PrimeFaces file upload section to browse and add a single file multiple times.
Your best bet is to bring in some JS/jQuery to remove all previously selected files when a new file is selected. Provided that you have given your <p:fileUpload> a widgetVar="upload", then this should do:
$(document).ready(function() {
upload.buttonBar.find('input[type=file]').change(function() {
if (this.value) {
var files = upload.uploadContent.find('.files tr');
if (files.length > 1) {
files.get(0).remove();
}
}
});
});
Works for me on PrimeFaces 3.5.
If you have file limit set to 1 and some error happens while file is loading - you have to refresh the page to get file upload work again. If you don't refresh the page you get out of limit error message.
I used solution with JS, like in accepted answer,but have to change selectors, because wigetWar did not work for me.
In my view i have :
<p:fileUpload id="objectUpload"... />
In my portlet theme, table with files has a css class of "ui-fileupload-files".
$(document).ready(function() {
$("div[id*='objectUpload']").find('input[type=file]').change(function() {
if (this.value) {
var files = $("div[id*='objectUpload']").find('.uifileupload-files tr');
if (files.length > 1) {
files.get(0).remove();
}
}
});
});
Hope it helps.I used PrimeFaces 6.0

Resources