I'm new to JSF and Primefaces and have just started working on logging in and basic navigation and I have run into a problem already. I've gone through about 10 similar questions here on SO and none of the solutions has worked for me yet so I figured I would post up my specific problem so that someone who really knows can point me in the right direction.
Logging in: seems to work just fine as does logging out but I'm concerned because the url in the browser still says that I'm at the login screen after logging in and I used the login example straight from the Oracle EE6 docs. Login method is provided below.
public String login(){
FacesContext context = FacesContext.getCurrentInstance();
HttpServletRequest request = (HttpServletRequest)context.getExternalContext().getRequest();
try{
logger.log(Level.FINE, "User credentials: name: {0}, password: {1}", new Object[] {this.username, this.password});
request.login(this.username, encrypt(this.password));
logger.log(Level.FINE, "User: {0} logged in", this.username);
}catch(ServletException e){
logger.log(Level.SEVERE, "User: {0} login failed, password: {1}", new Object[]{this.username, encrypt(this.password)});
context.addMessage(null, new FacesMessage("Login Failed!"));
return "error";
}
return "/faces/system/index";
}
After logging in I'm taken to the correct page in the correct directory and everything is being displayed corectly but when you hover over the links the status bar at the bottom of the browser displays the same url for all three links. Code for the page provided below.
<h:body>
<p:layout fullPage="true">
<f:facet name="last">
<h:outputStylesheet library="css" name="discovery.css"></h:outputStylesheet>
</f:facet>
<p:layoutUnit styleClass="headerDiv" position="north" size="100">
<h:graphicImage library="images" name="header.jpg"></h:graphicImage>
</p:layoutUnit>
<p:layoutUnit styleClass="navDiv" position="west" size="200" id="navPanel">
<h:form>
<h:outputText value="Navigation Menu"></h:outputText>
<br/>
<p:commandLink value="First Time Users" update=":main">
<f:setPropertyActionListener target="#{navigationBean.pageToDisplay}" value="tutorial.xhtml"></f:setPropertyActionListener>
</p:commandLink>
<br/>
<p:commandLink value="Help" update=":main">
<f:setPropertyActionListener target="#{navigationBean.pageToDisplay}" value="help.xhtml"></f:setPropertyActionListener>
</p:commandLink>
<br/>
<h:commandLink action="#{loginBean.logout()}" value="Log Out"></h:commandLink>
</h:form>
</p:layoutUnit>
<p:layoutUnit position="center" id="main">
<ui:include src="#{navigationBean.pageToDisplay}"></ui:include>
</p:layoutUnit>
</p:layout>
</h:body>
The NavigationBean
#Named(value = "navigationBean")
#RequestScoped
public class NavigationBean implements Serializable {
public NavigationBean() {
}
public String getPageToDisplay() {
return pageToDisplay;
}
public void setPageToDisplay(String pageToDisplay) {
this.pageToDisplay = pageToDisplay;
}
private String pageToDisplay = "welcome.xhtml";
}
When the page loads after logging in the default page set in the navigation bean is displayed but clicking on any link other than the log out link causes the default page to disappear from the center layout unit and a blank page is displayed/ Clicking on the log out link does log you out like intended though. ANy help would be greatly appreciated.
1. Logging in: seems to work just fine as does logging out but I'm concerned because the url in the browser still says that I'm at the login screen after logging in.
Send a redirect (this instructs the browser to send a new GET request on the given URL, which get reflected in browser's address bar).
return "/faces/system/index?faces-redirect=true";
2. After logging in I'm taken to the correct page in the correct directory and everything is being displayed corectly but when you hover over the links the status bar at the bottom of the browser displays the same url for all three links.
The <h:form> indeed submits to the very same page. Use <h:outputLink> or <h:link> instead of <h:commandLink> for page-to-page navigation. See also When should I use h:outputLink instead of h:commandLink?
3. When the page loads after logging in the default page set in the navigation bean is displayed but clicking on any link other than the log out link causes the default page to disappear from the center layout unit and a blank page is displayed
This is solved by using GET instead of ajax postback for page-to-page navigation. So, it's inherently solved when solving #2. You might only want to redesign your NavigationBean to be a filter or phase listener which also intercepts on GET requests. You shouldn't be navigating by POST at all. It defeats bookmarkability, user experience and SEO, exactly as you're encountering now.
Related
I have an issue with making a menu in my project using Primefaces. Actually, this menu will get me a possibility to show some small dialogs with settings for the workspace (by clicking on menu items). Each dialog should have data lazy loading from database. Unfortunately, when I include my dialogs in the page (single xhtml file or a couple xhtml files with ui:include), an event onShow happens on each page reload and this is wrong and provoke too many unnecessary requests to the database.
Here is an example:
UI part
<h:form id="form1">
<p:menubar id="mainMenu">
<p:submenu label="Main menu" icon="ui-icon-document">
<p:menuitem value="My settings" onclick="mySettingsWv.show()" url="#" />
</p:submenu>
</p:menubar>
</h:form>
<p:dialog id="mySettingsDlg" header="My Settings" widgetVar="mySettingsWv" resizable="false"
closable="true" modal="true" showEffect="fade" hideEffect="explode" dynamic="true"
closeOnEscape="true" onShow="#{mybean.onShow()}">
<h:outputLabel value="Settings dialog" />
</p:dialog>
ManagedBean part:
#ManagedBean (name = "mybean")
#ViewScoped
public class MyBean {
public static Logger log = Logger.getLogger(MyBean.class);
public void onShow() {
log.info("Method onShow is being called on each page reloading, but dialog still has not been shown");
}
}
If I use action "onclick" for <p:menuitem> for manual calling the necessary method, it still executes it for each page reloading. Also if I try use actionListener, action attributes it don't work. <p:ajax> cannot be attached to <p:menuitem>.
What should I do in that case? What is wrong can be in my code?
Have a look at the primefaces documentation for p:dialog (same problem with p:menuitem and onclick). There the documentation says about onShow:
Client side callback to execute when dialog is displayed. (emphasis added)
That means that you can specify a javascript function there, but it does not work that way to specify a action on your backingbean which is called everytime the dialog is shown. What happens in your case is the following: #{mybean.onShow()} is evaluated only when the file is parsed (i.e. the p:dialog is rendered into the HTML) and then the value which is returned by the method is inserted there (i.e. the empty String).
To fix this you have to define a javascript callback which makes the call on the bean. You can do this by using p:remoteCommand:
<p:remoteCommand name="onShow" action="#{mybean.onShow}"
partialSubmit="true" process="#this"/>
And then specify this callback as the onShow attribute:
<p:dialog id="mySettingsDlg" ...
onShow="onShow()">
Okay, so I have read through stackoverflow and various other portions of the net for a few days, and have not found a solution yet that fixes my issue.
Basically, I have an h:form in a p:dialog with two required fields. When the fields are both filled in, the form successfully submits and the dialog closes. When either or both fields are left blank, required field validation fails and updates the h:messages component in the p:dialog with the appropriate required messages. However, also when validation fails, the AJAX call seems to never "finish" and prevents subsequent calls from being executed. This is seen evident by my p:ajaxStatus component never disappearing from the screen, indicating that something is hanging somewhere. This is remedied by refreshing the page, at which point all other AJAX components begin to function again.
Additionally, I will note that this p:dialog is in a ui:define in a ui:composition, that dumps it into a master template. It is not nested within another h:form.
<p:dialog id="dlgDecision"
header="Decision"
widgetVar="dialogDecision"
modal="false"
resizable="false"
appendToBody="true">
<h:form id="fDlgDecision">
<h:messages id="msgDlgDecision" binding="#{msgform.messages}" errorClass="errormsg" infoClass="infomsg1" layout="table"/>
<h:outputFormat rendered="#{studentdetailsform.decisionAction == 'A'}">
<h:outputText value="Select an accept and admit code."/>
</h:outputFormat>
<h:outputFormat rendered="#{studentdetailsform.decisionAction == 'C'}">
<h:outputText value="Select a cancel and reason code."/>
</h:outputFormat>
<h:panelGrid columns="1">
<h:selectOneMenu id="apdcCode"
value="#{studentDetails.apdcCode}"
required="true"
requiredMessage="Please choose a decision code.">
<f:selectItem itemLabel="Select Decision Code"/>
<f:selectItems value="#{apdcCodes.apdcCodeList}"
var="apdc"
itemValue="#{apdc.apdcCode}"
itemLabel="#{apdc.apdcCode} - #{apdc.apdcDesc}"/>
</h:selectOneMenu>
<h:selectOneMenu id="admtCode"
value="#{studentDetails.admtCode}"
required="#{studentdetailsform.decisionAction == 'A'}"
requiredMessage="Please choose an admit code."
rendered="#{studentdetailsform.decisionAction == 'A'}">
<f:selectItem itemLabel="Select Admit Code"/>
<f:selectItems value="#{admtCodes.admtCodeList}"
var="admt"
itemValue="#{admt.admtCode}}"
itemLabel="#{admt.admtCode} - #{admt.admtDesc}"/>
</h:selectOneMenu>
<h:selectOneMenu id="wrsnCode"
value="#{studentDetails.wrsnCode}"
required="#{studentdetailsform.decisionAction == 'C'}"
requiredMessage="Please choose a reason code."
rendered="#{studentdetailsform.decisionAction == 'C'}">
<f:selectItem itemLabel="Select Reason Code"/>
<f:selectItems value="#{wrsnCodes.wrsnCodeList}"
var="wrsn"
itemValue="#{wrsn.wrsnCode}"
itemLabel="#{wrsn.wrsnCode} - #{wrsn.wrsnDesc}"/>
</h:selectOneMenu>
<p:commandButton id="decisionSubmit"
value="Submit Decision"
type="submit"
action="#{mainform.saveDecision}"
ajax="true"
partialSubmit="true"
process="#form"
update="#form msgDlgDecision"
oncomplete="if (!args.validationFailed) dialogDecision.hide()"/>
</h:panelGrid>
</h:form>
</p:dialog>
Some things I have already done in my debugging and troubleshooting:
- Moved the h:form into the p:dialog
- Made the backing bean with the values for the rendered attribute on the required fields ViewScoped (was having an issue with only some of the required messages showing, this resolved this problem)
- Added appendToBody="true" to p:dialog
- Added if (!args.validationFailed) to the oncomplete event of p:dialog
- Tried making the required fields NOT conditional (removed rendered attributes) to be sure this wasn't being caused by failed validation on non-rendered components (grasping at straws...)
EDIT: Here is a console dump from Chrome. Javascript error gets thrown when submitting the form with null required fields.
Uncaught SyntaxError: Unexpected token { jquery.js:14
bG.extend.parseJSON jquery.js:14
PrimeFaces.ajax.AjaxUtils.handleResponse primefaces.js:1
PrimeFaces.ajax.AjaxResponse primefaces.js:1
j.success primefaces.js:1
bZ jquery.js:14
b7.fireWith jquery.js:14
ca jquery.js:21
bZ
EDIT 2: Below are the only two JavaScript imports, both of which are in my template that is applied to the page via the ui:define and ui:composition mentioned above.
<h:outputScript library="primefaces" name="jquery/jquery.js" />
<script type="text/javascript" src="#{request.contextPath}/resources/scripts/jscript.js" />
The first import will force Primefaces to import jQuery into the page, even if there are no components on the page that utilize JavaScript. This is to allow my custom scripts in jscript.js to use jQuery reliably.
Other information:
JSF 2.1
Primefaces 3.4.2
Tomcat 6.0
Don't import jQuery.js manually as this is already done implicitly by PrimeFaces (it uses jQuery under the hood). You can remove that line.
Also you should see some examples of importing your custom js files with h:outputScript like this for example:
<h:outputScript library="scripts" name="jscript.js" />
but this is probably not reason of your problem, this is just hint for better JSF design.
A similar error showed up in Google Chrome, today, after I downloaded PrimeFaces 4.0 snapshot (2013-05-09) and tested my app.
Basically, I had to remove single quotes from data sent from bean to p:schedule via JSON. Since the error mentioned 'parse JSON' and "unexpected '" (unexpected single quote), I went to the datatable view of my data (instead of p:schedule) and recognized that some of the data had single quotes embedded. So, I removed the single quotes from the data, and that solved the issue for me.
Please click forum topic URL below.
PF 4.0 SNAPSHOT 2013-05-09: schedule not working
I've had the exact same problem at my application. The real problem was a duplicate jar problem.
I've had two primefaces jar in my application, one under the WEB-INF/lib directory, and the other under the ear/lib directory. Once I deleted the one under the WEB-INF, everything started to work as expected.
You could find the solution on the net via a search for the js exception.
Just would like to know why do I encounter this behavior in my application.
I used primefaces for the UI and almost all of my pages follows this pattern. I heavily used AJAX in all of my CRUD
operations and using dialogs to show it to the user.
<ui:composition template="myTemplate.xhtml">
<ui:define name="content">
<ui:include
src="/pages/CreateDialog.xhtml" />
<ui:include
src="/pages/UpdateDialog.xhtml" />
<ui:include
src="/pages/DeleteDialog.xhtml" />
</ui:define>
</ui:composition>
My only concern is that, after doing CRUD stuff in my dialogs and user accidentally clicks F5 or refresh on the browser,
FF/Chrome and other browser always mentioned
To display this page, Firefox must send repeat action...
Obviously this will cause double submit. Previously I used Post-Redirect-Get in this scenario in older apps but since this
is AJAX JSF update then I cannot do this.
What's the workaround for this and is this normal? I thought AJAX actions should not trigger again during browser refresh.
Help?
UPDATE
I am opening my dialog with this code
<p:commandButton value="Add"
onclick="createWidget.show();"
update=":CreateForm"
action="#{MyBean.add}"
/>
My create dialog uses this
<p:dialog header="Create">
<h:form id="CreateForm" prependId="false">
<p:commandButton value="Add" icon="ui-icon-plus"
actionListener="#{MyBean.add}"
update=":messageGrowl"
oncomplete="closeDialogIfSucess(xhr, status, args, createWidget 'createDialogId')"/>
</h:form>
</p:dialog>
I actually am following the pages from this site...Full WebApplication JSF EJB JPA JAAS
Already experienced a few times that having JavaScript errors in callback methods ends up in such behavior. I was able to reproduce your problem which is disappeared after correcting the callback signature:
oncomplete="closeDialogIfSucess(xhr, status, args, createWidget, 'createDialogId')"
accordingly to you JavaScript function signature:
function closeDialogIfSucess(xhr, status, args, createWidget, dialogid)
(Sure if your JavaScript call has only 3 parameters then correct the oncomplete call)
Unrelated: I guess that you are using this function to close a specific dialog. Another way doing it would be assigning widgetVar attribute to your dialog:
<p:dialog id="testDialog" header="Create" widgetVar="createWidget">
<h:form id="CreateForm" prependId="false">
...
</h:form>
</p:dialog>
The widgetVar object will represent your dialog in the callback function so you can close it by call the hide() function of dialog:
function closeDialogIfSucess(xhr, status, args, createWidget) {
if(args.validationFailed || !args.loggedIn) {
jQuery('#testDialog').effect("shake", { times:3 }, 100);
} else {
createWidget.hide();
}
}
I have a commandButton that will invoke a function to download a file (standard stuffs like InputStream, BufferedOutputStream ...) After download success, at the end of the function, I change some values of the current object and persist it into database. All of these work correctly. Now when file is done downloading, the content of the page is not updated. I have to hit refresh for me to see updated content. Please help. Below are the basic structure of my code
document: Managed Bean
getDrawings(): method return a list of Drawing (entity class)
CheckedOutBy: attribute of Entity Drawing
<p:dataTable id="drawing_table" value="#{document.drawings}" var="item" >
<p:column>
<f:facet name="header">
<h:outputText value="CheckedOutBy"/>
</f:facet>
<h:outputText value="#{item.checkedOutBy}"/>
...
</p:dataTable>
<p:commandButton ajax="false" action="#{document.Download}" value="Download" />
Inside my Managed Bean
public void Download(){
Drawing drawing = getCurrentDrawing();
//Download drawing
drawing.setCheckedOutBy("Some Text");
sBean.merge(drawing); //Update "Some Text" into CheckedOutBy field
}
You'd basically like to let the client fire two requests. One to retrieve the download and other to refresh the new page. They cannot be done in a single HTTP request. Since the download needs to be taken place synchronously and there's no way to hook on complete of the download from the client side on, there are no clean JSF/JS/Ajax ways to update a component on complete of the download.
Your best JSF-bet with help of PrimeFaces is <p:poll>
<h:outputText id="checkedOutBy" value="#{item.checkedOutBy}"/>
...
<p:poll id="poll" interval="5" update="checkedOutBy" />
or <p:push>
<p:push onpublish="javaScriptFunctionWhichUpdatesCheckedOutBy" />
Polling is easy, but I can imagine that it adds unnecessary overhead. You cannot start it using standard JSF/PrimeFaces components when the synchronous download starts. But you can stop it to let it do a self-check on the rendered attribute. Pushing is technically the best solution, but tougher to get started with. PrimeFaces explains its use however nicely in chapter 6 of the User Guide.
Well, I decided to go with BalusC's answer/recommendation above, and decided to share my code here for people that may stop by here, 'later'. FYI, my environment details are below:
TomEE 1.6.0 SNAPSHOT (Tomcat 7.0.39), PrimeFaces 3.5 (PrimeFaces Push), Atmosphere 1.0.13 snapshot (1.0.12 is latest stable version)
First of all, i am using p:fileDownload with p:commandLink.
<p:commandLink value="Download" ajax="false"
actionListener="#{pf_ordersController.refreshDriverWorksheetsToDownload()}">
<p:fileDownload value="#{driverWorksheet.file}"/>
</p:commandLink>
Since I have the xhtml above, and since p:fileDownload does not allow oncomplete="someJavaScript()" to be executed, I decided to use PrimeFaces Push to push message to client, to trigger the javascript necessary to unblock the UI, because UI was being blocked whenever I click the commandLink to download file, and for many months, I didn't know how to solve this.
Since I already am using PrimeFaces Push, I had to tweak the following on the client side:
.js file; contains method that handles messages pushed from server to client
function handlePushedMessage(msg) {
/* refer to primefaces.js, growl widget,
* search for: show, renderMessage, e.detail
*
* sample msg below:
*
* {"data":{"summary":"","detail":"displayLoadingImage(false)","severity":"Info","rendered":false}}
*/
if (msg.detail.indexOf("displayLoadingImage(false)") != -1) {
displayLoadingImage(false);
}
else {
msg.severity = 'info';
growl.show([msg]);
}
}
index.xhtml; contains p:socket component (PrimeFaces Push); i recommend all of the following, if you're are implementing FacesMessage example of PrimeFaces Push
<h:outputScript library="primefaces" name="push/push.js" target="head" />
<p:growl id="pushedNotifications" for="socketForNotifications"
widgetVar="growl" globalOnly="false"
life="30000" showDetail="true" showSummary="true" escape="false"/>
<p:socket id="socketForNotifications" onMessage="handlePushedMessage"
widgetVar="socket"
channel="/#{pf_usersController.userPushChannelId}" />
Months (or maybe a year-or-so) ago, i found it necessary to add the following to the commandLink that wraps p:fileDownload, which will refresh the file/stream on server, so you can click the file as many times as you need and download the file again and again without refreshing the page via F5/refresh key on keyboard (or similar on mobile device)
actionListener="#{pf_ordersController.refreshDriverWorksheetsToDownload()}"
That bean method is referenced whenever enduser clicks the commandLink, to download file, so this was perfect spot to 'push' a message from server to client, to trigger javascript on client, to unblock UI.
Below are the bean methods in my app that gets the job done. :)
pf_ordersController.refreshDriverWorksheetsToDownload()
public String refreshDriverWorksheetsToDownload() {
String returnValue = prepareDriverWorksheetPrompt("download", false);
usersController.pushNotificationToUser("displayLoadingImage(false)");
return returnValue;
}
usersController.pushNotificationToUser(); i had to add this one, tonight.
public void pushNotificationToUser(String notification) {
applicationScopeBean.pushNotificationToUser(notification, user);
}
applicationScopeBean.pushNotificationToUser(); this already existed, no change to this method.
public void pushNotificationToUser(String msg, Users userPushingMessage) {
for (SessionInfo session : sessions) {
if (userPushingMessage != null &&
session.getUser().getUserName().equals(userPushingMessage.getUserName()) &&
session.getUser().getLastLoginDt().equals(userPushingMessage.getLastLoginDt())) {
PushContext pushContext = PushContextFactory.getDefault().getPushContext();
pushContext.push("/" + session.getPushChannelId(),
new FacesMessage(FacesMessage.SEVERITY_INFO, "", msg));
break;
}
}
}
You could use the update attribute of the p:commandButton component to re-render the area that you intend to refresh after download, in this case your "drawing_table".
<p:commandButton update="drawing_table" action="#{document.Download}" value="Download" />
As Balusc answered, we cannot get response twice from a single request.
To refresh a page after download, better use the following java script in download link(p:commandbutton) onclick tag.
Example:
<p:commandButton ajax="false" icon="ui-icon-arrowstop-1-s" onclick="setTimeout('location.reload();', 1000);" action="#{managedBean.downloadMethod}" />
this will automatically refresh the page after 1 second, at the same time i.e. before refresh, you will get the download file, based on your download response time, increase the seconds in that script. Seconds should not less than that download response time.
I will try to be as brief as possible, please stay with me here
"A.jsf" -> managed bean : bean
"#{bean.list}": will take us to B.jsf
<p:growl id="msgs" showDetail="true"/>
<h:form id="myform1" enctype="multipart/form-data">
<p:panel header="Upload" style="font-size: 11px;">
<h:panelGrid columns="2" cellpadding="10">
<h:outputLabel value="Drawing:" />
<p:fileUpload fileUploadListener="#{bean.handleFileUpload}" update="msgs" allowTypes="*.*;"/>
</h:panelGrid>
<p:commandButton ajax="false" immediate="true" id="back" value="Back" action="#{bean.list}"/>
<p:commandButton ajax="false" id="persist" value="Persist" action="#{bean.handleRevision}" />
</p:panel>
</h:form>
Then the handleFileUpload()
if(!upload){
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", "You do not have permission to upload.");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
...
"B.jsf" -> managed bean: bean2
...
<p:growl id="msgs" showDetail="true"/>
...
When I click upload, it give me a growl error message "You do not have permission to upload.", which is good. But then when I click "Back", which will take me to B.jsf, I see the growl message "You do not have permission to upload." again. What seem to be happening is as I click the "Back", I send other form request to upload, which then generated the same error message, which then being displayed at B.jsf. Is there a way to fix this, beside putting the "Back" button into an empty form, because now I have two buttons standing on top of each others, instead of side by side. I try to do this:
FacesContext.getCurrentInstance().addMessage("tom", msg);
hoping that it would send to component with id="tom", so then the growl with id=msgs, would not get load, but no luck. I try to turn the upload flag on when I click the Back button, but the web form get requested before the method that handle the back navigation get called.
It is not as brief as I want it to be, therefore I want apologize for it :D
beside putting the "Back" button into an empty form, because now I have two buttons standing on top of each others
The HTML <form> is by default a block element. HTML block elements are by default been placed in a new line. You actually want to make it an inline element. You can do this using display: inline; in CSS.
Back to the actual problem, it however surprises me that the fileUploadListener method is called in spite of the immediate="true" in the p:commandButton. I tried to reproduce this and I can confirm this. But I wouldn't expect it to happen. Normally the immediate="true" on a button is the solution to skip submitting of the "whole" form (at least, skip the UIInput components without this attribute). Further investigation learnt me that the p:fileUpload isn't an UIInput component at all and that the listener is fired during apply request values phase instead of validations or update model values phase. So this behaviour is fully predictable, but imo still an oversight in the design.
Since the p:fileUpload requires ajax="false" on the p:commandButton component, you can on the other hand also just remove it from the back button so that it fires an ajaxical request and hereby skips the fileUploadListener being called.
Actually, putting the button in a different form sounds like an excellent solution. The reason the buttons don't align any more is that the new starting <form> element starts on its own line. You should be able to prevent this by adding form { display: inline; } to your CSS file.
That said, if you have some leftover error messages that you want to get rid of, you can do this in the initializing method of your backing bean (if you have one). The following works peachily:
public void clearErrorMessages() {
//it may get messy to debug why messages are swallowed
logger.debug("clearing messages, coming from " + new Exception().getStackTrace()[1]);
Iterator iter = FacesContext.getCurrentInstance().getMessages();
while (iter.hasNext()) {
FacesMessage msg = (FacesMessage) iter.next();
logger.debug("clearing message: " + msg.getDetail());
iter.remove();
}
}
The disadvantage here is that any errors that occur between submitting the form and initializing the backing bean of the target page are also swallowed.