Confused about JSF Lifecycle - jsf-2

I want to show a page that renders a list of 'Group' entities, so my page (xhtml) has this in it
<f:event type="preRenderView" listener="#{groupController.loadGroups}" />
This is called when the page loads, and yesterday it was returning a list as expected. The method looks like this
public void loadGroups() {
allGroups = groupDao.findAll();
}
Now I added a form to save a new group (on the same page). When I save that form, it calls this method:
public String save() {
groupDao.create(group);
return "/admin/groups?faces-redirect=true";
}
I have confirmed that the group is saved in the database. But, I was expecting the "return" statement to reload the page as if entering it for the first time, calling the 'preRenderView' method etc etc.
When it comes back into the page it does call that method, but am using a datalist to display the collection of groups returned and when that makes a call to
public Collection<Group> getAllGroups() {
return allGroups;
}
... the 'allGroups' variable has been reset back to null.
I haven't touched JSF for a couple of months and I think I used to understand all this but I obviously don't. Could anyone give me a clue what I'm doing wrong?
If I find the answer (digging my books out right now) I will post an update. Thanks though.
Update: just to clarify, I thought the loadGroups method would be executed before the getAllGroups method, since it's a preRenderView event. But they seem to be getting executed the wrong way around (getAllGroups is called first, before loadGroups() has populated the collection). It's a ViewScoped bean by the way.

I'm not sure why my original version doesn't work, but if I get rid of the preRenderView statement in my page, and add a #PostConstruct annotation on the loadGroups method, it works fine. I understand why the #PostConstruct method would work, but not why the preRenderView doesn't work. Anyway, its working if I do that...

Related

OmniFaces' enableRestorableView restores empty view root

I have a page anchors.xhtml which contains a form and an
<h:commandButton action="#{anchorsBean.onRefresh}" value="Refresh" />
which submits to the same page using redirect:
public String onRefresh() {
refresh();
return "anchors?faces-redirect=true";
}
I want to avoid ViewExpiredException on session timeout or caused by other reason. For that purpose I get use of OmniFaces' restorable view handler:
<f:metadata>
<o:enableRestorableView />
</f:metadata>
But surprisingly at this point, in case of expired view, my redirection behavior gets lost: POST request/response simply takes place. Some digging into details reveals me that navigation/redirection is normally triggered by UICommand at Invoke Application phase, however the view root restored by <o:enableRestorableView /> seems to have no children at all. So there's simply no command component to call the default action listener which must handle redirection.
I am using Mojarra 2.1.19 and OmniFaces 1.4.1. I can see in the sources that RestorableViewHandler creates the view but does not build it: can it be the reason for the empty UIViewRoot? Evidently, there's something I am flagrantly missing and misunderstanding...
Most interesting, when I use no <o:enableRestorableView /> but simply remove ViewExpiredException event in my custom exception handler, everything works fine: FacesContext happens to somehow already contain a fully built UIViewRoot (which <o:enableRestorableView />, when included, will later replace with an empty one). So this way I'm getting the originally desired behavior, at least things look like that. But this way is definitely wrong I suppose.
Many thanks in advance for any clarification on this.

JSF ui:debug causing NullPointerException

My JSF application has a <ui:debug> tag in the top level template. I have a data item creation page that saves the item to the DB. The DB then returns the saved item which I store in the JSF flash. I then redirect to a new page and display the item that I retrieved from the flash. When I view the page, the Debug page is shown with a NullPointerException and sometimes there is no stack trace and other times there is a stacktrace similar to (don't pay attention to the specific line numbers.
NullPointerException
at com.sun.facelets.util.DevTools.writeVariables(DevTools.java:158)
at com.sun.facelets.util.DevTools.writeVariables(DevTools.java:144)
at com.sun.facelets.util.DevTools.debugHtml(DevTools.java:135)
at com.sun.facelets.tag.ui.UIDebug.writeDebugOutput(UIDebug.java:92)
at com.sun.facelets.tag.ui.UIDebug.encodeBegin(UIDebug.java:81)
...
If I remove the <ui:debug> tag, then my page is successfully displayed. What is causing the NullPointerException?
Part of the job of the <ui:debug> tag is to display the contents of all the various JSF scopes including the "flash scope". DevTools.writeVariables is a helper function that is used to turn the objects in the scopes into something readable to display in the debug page. It uses methods like toString() to display the objects. The object that was stored in the flash, overrode the toString() method with the following boilerplate code
#Override
public String toString() {
//TODO: Supply implementation
return null
}
Since the toString() was returning null, it later caused a NullPointerException. If you properly implement toString(), this error will not occur.

jsf clearing the form

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.

Context parameter not working in Tapestry 5.3.2 for Submit

I have a loop displaying records, and I want to add submit buttons to all the rows. The reason they need to be submit is because there is a form at the bottom I would like to have persisted when the user selected one of the buttons.
I've seen the comments about using defer, etc, but nothing seems to work for me. The current submit code I'm trying is:
<input t:id="deleteItem" t:type="submit" t:context="item.number.content" value="deleteItem" class="deleteItem" />
To expand on the context:
The current context I have listed, is just a string within the number object within the item object. In fact, it's being displayed in the code above perfectly fine.
To test this differently, I've replace item.number.content with a getContext() method and had it return a hard-coded 1. I then debug this method and see it being called when the page is SUBMITTED, not when the page is rendered as I would have expected.
The context is never populated until after the button is pushed. Am I misunderstanding something??
Edit:
So my issue is with getting the context value. Take for instance my code:
<t:loop source="itemsList" value="item" formState="none">
<!-- Display info here -->
<input t:id="deleteItem" t:type="submit" t:context="myContext" value="deleteItem" class="deleteItem" />
</t:loop>
The definition for getMyContext is:
public String getMyContext() {
String context = item.getNumber().getContent();
return context;
}
The problem is, the method is not called until after the submit has been pressed at which time the variable "item" is null. I was expecting getMyContext to be called each time the submit button is rendered, and then when the submit is selected, the event would be triggered with the appropriate context. Does that make sense?
I finally figured out what my problem was:
formState="none"
I originally did this because it was having trouble translating between my Objects and a String. When this is selected, the submit buttons don't work properly. I ended up changing it to formState="iteration" and now it is working exactly as I expected it to (and I needed the defer as expected too).
Try adding the following to the page/component you add this t:submit to:
#OnEvent(component = "deleteItem")
private void handleSubmit(Integer contextValue) {
//Do whatever you need to do with the passed context value here.
//Most commonly you would store the context in a java page/component field to
//be used by the form eventhandler to do some sort of CRUD
}
The context value will not get written out while rendering the page, it will be passed as a parameter to your event handler while submitting the form. So what you are seeing is correct behavior.

MVC Controller Called twice

I have a Product controller with Index action, which basically creates the view form for post and Index (Post action verb) action on the ProductController which basically save the product to db but when validation errors occur, I am returning a View(mymodel) else when saved, I am returning RedirectToAction("Created,"Product") but for some odd reason when I break into the code , it is hitting the Product Controller action twice rather than just once. Hence the product has 2 records instead of one.
public ActionResult Index()
{
return View()
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(FormCollection fc)
{
// 2 calls are made to this controller
try
{
// save the product
return RedirectToAction("Created");
}
catch(Exception ex)
{
// recreate the model from form collection
return View(viewData); // when a validation error occurs it comes into the catch block
}
}
Sometimes I have found Firebug to cause this behavior. Try disabling its Script panel, if you have it installed.
Explanation: In some cases Firebug isn't able to get the script sources for the display within its Script panel. In these cases it initiates a second request to get them. See issue 7401 for some discussion about this, which alleviates the problem and is fixed with Firebug 2.0.2.
Here's a basic checklist (copied from here):
Check that you don’t have any image or another elements in the View with an
empty src attribute (<img src=”" /> for example) or have src
attribute referencing something that no longer exists. You better
check directly in the browser's “Page Source”, than in the View itself due to the
possibility of some “dynamic” issues when the View is rendered. Once you
find such empty element in the page's HTML source its usually trivial to
find the same element in your View and fix the issue. This can also happen with <link href="">.
Check that you don’t have any AJAX calls referencing an empty URL (browsers will interpret such empty URL as the current page and will request the current page again making the Controller action execute few times).
You forgot to return “false” from the JavaScript click event handler for a link or button that makes an AJAX call. If you forget to “return false”, the browser simply interprets the default action of the link – regular, non AJAX, calling the same page again)
Sometimes Firebug and YSlow [ Firefox (FF) plugins ] can cause such issues. Just temporarily disable them in FF or test with a different browser.
Watch out for duplicate filters decorating your controller or action. (this was my problem)
Another solution for this case..
I had exactly same problem, running and testing from Chrome. I couldn't debug it because the second call was coming from (external call). I have randomly tested it in Firefox and Internet Explorer where there was no double hit.
Whatever nasty thing it was, I have deleted Chrome cache (everything!!!) and problem has been resolved.
Hope it will help some of you :)
I had a similar issue with a controller action that generated an image, and I was only seeing it with Firefox. There is a really old bug that causes this, that I guess is still there.
https://bugzilla.mozilla.org/show_bug.cgi?id=304574

Resources