2

I have been using preRenderView (+ the re-direct and skip postback hacks) in my app and I'm trying to replace it with viewAction that comes with JSF2.2 (MyFaces 2.2.4). However I found it triggers earlier than command actionListener and action that it's rather useless for my purpose. For example:

import java.io.Serializable;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

@ManagedBean
@ViewScoped
public class TestBean2 implements Serializable {

  int count = 0;

  public void actionFunc() {
    System.out.println(count);
  }

  public String addCount() {
    count++;
    return null;
  }

  public int getCount() {
    return count;
  }

  public void initView() {
    System.out.println("initView");
  }

  public void setCount(int count) {
    this.count = count;
  }

}

and then a very simple .xhtml:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core" xmlns:pt="http://xmlns.jcp.org/jsf/passthrough" xmlns:jsf="http://xmlns.jcp.org/jsf"
  xmlns:t="http://myfaces.apache.org/tomahawk" xmlns:p="http://primefaces.org/ui">

<f:metadata>
  <f:event type="preRenderView" listener="#{testBean2.initView}" />
  <f:viewAction action="#{testBean2.actionFunc}" onPostback="true" />
</f:metadata>

<ui:composition template="/WEB-INF/template.xhtml">
  <ui:define name="title">Test</ui:define>

  <ui:define name="content">
    #{testBean2.count}
        <h:form>
      <h:commandButton styleClass="btn btn-primary" action="#{testBean2.addCount}" value="addCount">
      </h:commandButton>
    </h:form>
  </ui:define>
</ui:composition>
</html>

So the addCount() System.out output is always 1 behind what's on the rendered view, because actionFunc() always triggers before addCount().

If I want to do something like, if (count == 3) do a return "newpage" for a redirect, it ends up way too early for such decision. I can do the check in initView() and do ConfigurableNavigationHandler.performNavigation("newpage"), but that's the kind of hack I'm trying to eliminate with JSF 2.2.

Therefore, it seems like viewAction is rather useless if I want it to work with any value that I'm going to change with action or actionListener. Is it supposed to work like this?

Thanks!

JPlatinum
  • 21
  • 3
  • This question sounds interesting, have you read in the [docs](http://www.oracle.com/technetwork/articles/java/jsf22-1377252.html) about the `f:viewAction` tag? There are other attributes such as `phase` or `immediate`, give them a try and don't forget to publish an answer if you happen to achieve it! – Aritz Jul 07 '14 at 21:05
  • Hi. Already tried. phase by default is already at the latest possible: "APPLY_REQUEST_VALUES, PROCESS_VALIDATIONS, UPDATE_MODEL_VALUES, or INVOKE_APPLICATION. The default is INVOKE_APPLICATION." immediate="true" makes it APPLY_REQUEST_VALUES, which is even earlier what it is now. – JPlatinum Jul 07 '14 at 21:11
  • I began writing a comment, but I ended up writing a whole answer with the way I think it's intended to work. Hope it'll be useful for you. – Aritz Jul 07 '14 at 21:25

1 Answers1

1

preRenderView event is executed at the beginning of the RENDER_RESPONSE phase. However, the viewAction tag does not allow that phase. I suppose that in your case you should handle the redirection in your action method (TestBean2#addCount) and forget about using events, cause it makes no sense at all to perform a check over a value that has been just initialized (that's what you do with the preRenderView method, when you load the view at first time). Checking it into the action method, after having performed your logic, seems the natural way for it.

I believe events can be used to check conditions as well, but they have more to do with session and application states and not with your current view values. I mean, you could do this to handle a redirection when no items available in your shop, before your view gets rendered:

public String checkItemsAvailable() {
    if (!dao.itemsAvailable()){
        return "noItemsAvailable";
    }
    return null;
}

But for a redirection based in a condition that's being evaluated in your current view, performing it in the action method is the best way to go:

public void addCount() {
    count++;
    if (count>3){
        FacesContext.getCurrentInstance()
            .getExternalContext().redirect("index.xhtml");
    }
}

See also:

Community
  • 1
  • 1
Aritz
  • 30,971
  • 16
  • 136
  • 217
  • Hi. Thanks for the answer. In the actual case, the action method actually lives inside of a SessionScoped bean and changes a value within it (say, a mode A/B toggle) and returns to the same view (it always return null). My intention is to have the viewAction in each page to check that, and do things accordingly (like, redirect). Therefore, I can't hard-code a non-null return in the action method. With what it is now, the viewAction method always sees that value 1-step behind compared to what preRenderView sess, which is the correct value. – JPlatinum Jul 07 '14 at 22:08
  • Also, further testing shows if the action goes to a different view, then viewAction is triggered after the action. However if the action goes back to the same view (i.e., return null;), viewAction (again, with postback=true) triggers BEFORE the action. – JPlatinum Jul 07 '14 at 22:46
  • I don't know what your concrete case is, however, having seen the viewaction documentation, I infer you can use it to redirect based in conditions that were set before current request. If you want to redirect basing in current request, use a redirection inside your current button methods. Anyway, you always have the choice of keeping as you were doing with prerenderview. – Aritz Jul 08 '14 at 04:35
  • @JPlatinum, your second comment makes sense if the different view you're going to has a `viewAction` tag too. But that's just because it's another JSF cycle what you are performing (the `viewAction` in the destination view will be called after first view's action). – Aritz Jul 08 '14 at 06:01