I need help. I'm new to JSF and im using JSF2 and richfaces in my project.
I want to clear the form for which I'm using <f:ajax render="#form"/> in refresh button. I have an ADD button on that screen which adds one record and I hit refresh then it's going to the default page. But when I once again go to enter a record then those values which I entered earlier remain in the form fields.
Can anyone please help me out with this issue?
Assuming that you mean the browser's refresh button when you say "I hit refresh", then that can happen if you've incorrectly placed the bean holding view scoped data in the session scope. You're then basically reusing the very same bean as the form is previously been submitted to. Putting the bean in the view scope instead of the session scope should fix this problem. The view scope ends when you navigate to a different page or fires a new request (as by hitting browser's refresh button).
See also:
How to choose the right bean scope?
Update if you're due to bad design restricted to using session scope, then you might want to hack this around by a
<f:event type="preRenderView" listener="#{sessionScopedBeanWhichShouldActuallyBeViewScoped.resetModel}" />
with
public void resetModel() {
if (!FacesContext.getCurrentInstance().isPostback()) {
model = null;
}
}
This will clear the model on every GET request. However, regardless of this hack, you'll still run into serious problems when the enduser opens the same view in a different browser tab/window within the same session.
The right solution is to put the bean in the view scope instead of the session scope, as said earlier.
Related
I've created a bean to search an sql table. This bean produces an arraylist that is displayed in a rich:dateTable. For each row in this table, there is a column that is a link to another page that specifies which record in the table is to be displayed, e.g.:
<h:link id="profile_last_name" value="#{record.string}" outcome="#{search.action()}">
<f:param name="user_id" value="#{record.getInteger('user_id')}"/>
</h:link>
The search bean is passed a navigation outcome, e.g. "staffEditUser" that creates a URL like:
http://localhost:8080/staff/edit/user.xhtml?user_id=98
I'd like to change the search bean so that if there is only one row in the search result, it immediately goes to the edit page. I know how to do the redirect if I knew the destination page name but I don't; all that I know inside the search bean is the navigation outcome. Is there some way to access the navigation rules from inside the bean?
Alternatively, and this seems very kludgy to me, could I simply add parameters to a redirect in the xhtml file that would be processed by the jsf navigation? If so, how?
Thanks very much for any help.
You can redirect the JSF page from within your managed bean method with
FacesContext.getCurrentInstance().getExternalContext().redirect("url");
As I understand you. You should check inside your ManagedBean database read method if the returned results have the size of none and then redirect to the edit page of this record.
I've looked at some other threads here and I think I have the general idea of how to use a SessionScoped bean for maintaining a user's logged in state. However, as I am relatively new to JSF 2, I am trying to figure out the best way to use the bean during login and on subsequent pages to render a header/footer (using same page template, but different menus/links depending on login state and non-logged in state).
I have a typical JSF2 login form that calls a backing bean (request scoped) login() method. All of that works, and I am going to tie it in to the container security soon as I have a little more time to work on and figure that part out (realms, roles, etc).
In the old pre-JSF (jsp/struts) days, upon login, I'd add an object, usually a user ID Long value that would be easy to replicate across a cluster of sessions and restore a full user object from. This kept the httpsession data minimal (one Long value per user), and regardless of what server a user was routed, I could determine they were logged in and pull up their user object and go from there.
With JSF, I am not sure the right way to do this. I have a SessionScoped bean with a Long userid property. Upon the login() method succeeding, I use the ExternalContext to add the attribute of the SessionScoped object, something like:
User user = loginBean.login(username, password);
Session session = new Session();
session.setUserid(user.getId());
externalContext.getSessionMap().put("usersession", session);
externalContext.redirect(originalURL);
The Session is the SessionScoped bean, and it's CDI name is usersession. I hope that is the right way to stick the bean into the HttpSession so it can be used on pages, etc with EL.
The first thing I am not sure of is because the bean is SessionScoped, do I need to put it into the session after creating it in the above code? Or is that done automatically upon creation for me since it's scoped as session?
The second question is.. being picky that I am, I don't want a Session object in the session until the user logs in, even if the userid is null. So on a xhtml page, if I have something like:
<h:panelGroup render="#{usersession.loggedin}"...>
Will that create and put the Session bean into the session the first page that uses that? Or will that Session object not be put into the HttpSession until I do so with my code above? My guess is, if it gets put into the session upon creation, then the use of it on any page will create it and stick it in the session. I am less bothered by this..the object with just the Long property is very little memory use per user on the system, but like I said, I am being picky and more so wanted to understand exactly when a SessionScoped object gets stored into the session.
Thanks.
The first thing I am not sure of is because the bean is SessionScoped, do I need to put it into the session after creating it in the above code? Or is that done automatically upon creation for me since it's scoped as session?
The managed bean is autocreated in the given scope if referenced for the first time in EL by #{beanName} while it's not in the scope yet. So, no, you don't need to do it yourself. Just inject it as managed property in your login bean.
#ManagedProperty("#{usersession}")
private Session session;
// ...
User user = loginBean.login(username, password);
session.setUserid(user.getId());
externalContext.redirect(originalURL);
Will that create and put the Session bean into the session the first page that uses that? Or will that Session object not be put into the HttpSession until I do so with my code above?
It will do that once you reference it for the first time in EL by #{beanName}.
Unrelated to the concrete question, another way is
User user = loginBean.login(username, password);
externalContext.getSessionMap().put("user", user);
externalContext.redirect(originalURL);
and check for the logged-in user as follows
<h:panelGroup rendered="#{not empty user}">
See also:
Performing user authentication in Java EE / JSF using j_security_check
I have a sample use case: I have an edit page that use GET parameter "id".
eg. edit?id=1
This edit page is backed by a ViewAccessScoped (CODI) Bean.
In this edit page, I have a datatable with links that link to the same "edit" page, but with another id. (eg. edit?id=2)
<h:link value="#{mecaPart.id}" outcome="edit" target="_blank">
<f:param name="id" value="#{mecaPart.id}" />
</h:link>
The problem, is that the window open correctly, but it is the same bean that is used! And so I am editing the same part...
I have placed a log in #PostConstruct, and it is the same bean reference that is called multiple times. (even with the new ID!)
My question, how can I tell JSF to create a new ViewAccessScoped backing bean when I click the link, and not re-use the actually used one?
Finally, I discovered that #ViewScoped CODI bean did not preserved the backing bean from page refresh. So, I have to use ViewAccessScoped.
According to Gerhard Petracek: http://os890.blogspot.fr/2011/08/scopes-view-scope-vs-view-access-scope.html
the view-scope of jsf2+ is bound to a concrete jsf page. that means: as soon as you navigate to a different page, the state gets lost. that's better than nothing, but not useful for a lot of use-cases. the main use-case which needs it are ajax-requests on a page and the data used by them aren't needed on other pages, but it's pretty easy to break it e.g. with a browser-refresh on a page which stores the data in a view-scoped bean and has no form with input components. (in a previous blog post i described how to use the infrastructure provided by codi to create a session based view-scope to overcome such disadvantages cause by storing view scoped beans as part of the tree-state.)
like with the view-scope view-access-scoped beans are available on a page, but they also exist for the next page. that means: they are forwarded to the next page and get destroyed autom. if they don't get used during the first request of the next page. that's e.g. useful for wizards. if you have a wizard page which doesn't use the bean or you have to support the possibility to interrupt a wizard, you can use the grouped-conversation scope (and even the window-scope) provided by codi. however, due to the powerful api of codi you can also destroy the scope manually at any time (if needed).
So, to solve the problem of opening a new tab with another "ID", I had to set "CODI Client Side WindowHandler", according to the CODI Wiki.
https://cwiki.apache.org/confluence/display/EXTCDI/JSF+WindowHandler
So I added:
<alternatives>
<class>org.apache.myfaces.extensions.cdi.jsf.impl.scope.conversation.ClientSideWindowHandler</class>
</alternatives>
To the file beans.xml, and I used #ViewAccessScoped. Everything is working smoothly now.
You can use #ViewScoped which also works in CODI
I need a way to manage the creation of the commandlink ID in DataTable, the problem is that when i use it for deleting a record and interrupt the rendering of the page (reloading it by 'ctrl+f5' ) it assigns the same id to another link button, which results in deleting the row containing it.
The problem in question is sound, but the requested solution is not the right one and not easily to achieve in JSF — basically, you'd need to homebrew a custom command link renderer which is designed specifically for usage in data tables and is able to recognize the specific entity.
The right solution is send a redirect to the same view after POST.
public String delete() {
// ...
return FacesContext.getCurrentInstance().getViewRoot().getViewId() + "?faces-redirect=true"; // Feel free to hardcode the view ID, though.
}
(if you intend to display some faces message along it, use the flash scope)
A browser refresh would then result in only the redirect being refreshed rather than the POST action.
An alternative is to submit by ajax instead.
<h:commandLink ...>
<f:ajax execute="#form" render="#form" />
</h:commandLink>
A browser refresh would then only re-execute the last synchronous request, which would be the initial GET request which opened the page in question.
I would like to add to my web app a dynamic breadcrumb using the Primefaces component. I've created a model to push items on the breadcrumb so that when one of its links is followed, the trailing links are removed. This works in most scenarios, but sometime the bradcrumb doesn't behave the way I expect. Basically, in order to track the landing page, I've added a preRenderView listener on each navigable page and implemented the model update logic in a session scoped bean.
<f:event type="preRenderView" listener="#{bcb.onRenderView}" />
<f:attribute name="pageName" value="ThisPage" />
The listener receives the page name as an attribute and obtains the complete URL (including query string) from the external context; these information, along with a unique id created from the UIViewRoot, are used to build a BreadCrumbItem that is pushed on the model:
public void onRenderView(ComponentSystemEvent evt) {
UIViewRoot root = (UIViewRoot)evt.getSource();
final String reqUrl = FacesUtils.getFullRequestURL();
String pageName = (String) evt.getComponent().getAttributes().get("pageName");
if(pageName != null) {
model.push(new BreadCrumbItem(root.createUniqueId(), pageName, reqUrl));
} else {
model.reset();
}
}
The push() and reset() methods of the model are implemented as follows:
/**
* When a link is pushed on the bread crumb, the existing items are analyzed
* and if one is found to be equal to the pushed one, the link is not added
* and all the subsequent links are removed from the list.
*
* #param link
* the link to be added to the bread crumb
*/
public void push(BreadCrumbItem link) {
boolean found = removeTrailing(link);
if(!found) {
addMenuItem(link);
}
}
/**
* Reset the model to its initial state. Only the home link is retained.
*/
public void reset() {
BreadCrumbItem home = new BreadCrumbItem();
removeTrailing(home);
}
Is this approach feasible? Can you suggest some better way to track page navigation without the need to leverage a life cycle listener? Thanks a lot for your help.
I have implemented my own one for my web app, in my case I didn't use the p:breadCrumb component because it's implemented using buttons.
Basically, I have an #SessionScoped bean which contains a stack (navigation stack), storing all the url's you have in the breadcrumb and the params for each of them. The view (xhtml) part is composed by p:button elements, which have the outcome of the stack's stored urls.
When you navigate to an url, corresponding bean's f:event type="preRenderView" is called (as the way you're doing it) and the bean takes the params from the url, after that it establishes itself into the stack (not the bean itself, cause it's #ViewScoped and going to be destroyed, just the url and params).
In case you click on a back button in the breadcrum, you send an additional param which indicates the index of the button. Based on that index, the destination bean knows that you're trying to recover such view, so asks the navigation stack for that view params and navigation stack removes navegables which are after it.
It took me a while, but it's fully functional. Good luck.
Edit
Be careful when using session scope for saving the current navigation state. It'll have influence in all the opened tabs, so probably it's not what you want, unless you expect your end user to use your application in one tab only. Anyway, the general usability guidelines say you should use categories instead of navigation paths for your breadcrumbs (HTTP is stateless and the history itself is kept by the browser). So a dynamic breadcrumb doesn't make sense anymore, at least if you're using different urls for your views.