views:

2552

answers:

3

I have this backing bean:

public class PageBean 
{
    private List<SelectItem> selectPages;
    private List<SelectItem> selectRowsPerPage;
    private String selectedPage;
    private String selectedRowsPerPage;
    private int pages = 0;

// getter methods
public boolean getRender()
{
 boolean rendered = pages > 0? true: false;
 return rendered;
} 

public List<SelectItem> getSelectPages() 
{
    int value = 0;

    selectPages = new ArrayList<SelectItem>();
    for (int i = 1; i < (pages + 1); i++) 
    {
        if (i > 1) { value = i * 10; }
        selectPages.add(new SelectItem(Integer.toString(value), Integer.toString(i)));
    }   

    return selectPages;
}

public String getSelectedPage()
{
    if (selectedPage == null) {
     selectedPage = "1";
    }

    return selectedPage;
}

// setter methods
public void setSelectPages(List<SelectItem> selectPages) {
    this.selectPages = selectPages;
}  

public void setSelectedPage(String selectedPage) {
    this.selectedPage = selectedPage;
}    

// action methods
public void changePage(ValueChangeEvent event)
{
    PhaseId phase = event.getPhaseId();

    if (phase.equals(PhaseId.INVOKE_APPLICATION)) {
     System.out.println((String) event.getNewValue());
     setSelectedPage((String) event.getNewValue());
     FacesContext.getCurrentInstance().renderResponse();
    } else {
     event.setPhaseId(PhaseId.INVOKE_APPLICATION);
     event.queue();
    } 
}    
}

And the h:selectOneMenu is:

<h:selectOneMenu id="page" value="#{pageBean.selectedPage}"
   valueChangeListener="#{pageBean.changePage}" onchange="submit()">
   <f:selectItems value="#{pageBean.selectPages}" />
</h:selectOneMenu>

The above codes for the changePage() method do not return the new selected page value from the h:selectOneMenu. Instead, it returns the page value prior to submit. I don't understand why.

Can someone please help? Have been stuck on this for 2 days now.

+1  A: 

Apparently you're doing a redirect instead of a forward, while the bean is request scoped. A redirect creates a new request, hereby garbaging all initial request scoped attributes, including request scoped beans. It will thus cause recreation of request scoped bean with all default properties. To solve this problem, remove the <redirect/> entry from the <navigation-case> entry in faces-config.xml, if any.

If this is for pure page-to-page navigation, I'd recommend another approach for this. Get rid of the valueChangeListener, have the page URL's as SelectItem values and replace submit() with:

onchange="window.location=this.options[this.selectedIndex].value"

You namely don't want to use POST for simple page-to-page navigation.

Hope this helps.

Edit: as per the comments, you actually want datatable pagination and this dropdown must instantly go to page X of the datatable at the same JSF page? In this case, forget my answer above, it wasn't clear from your question and I didn't realize that you want datatable pagination.

The as far posted information and code looks fine. Have you debugged the code to see what happens with property values? Isn't it nulled out afterwards? Is the correct value returned during render? Does the dropdown list return the expected items during render? That kind of trivial things. Maybe something else which you didn't post about is colliding with this all.

Edit 2: I actually created an SSCCE to see if it really works and as I expected, it just works. Your problem lies somewhere else. Maybe a buggy Converter? Maybe it was actually a redirect?

MyBean (request scoped):

public class MyBean {

    private Integer page;
    private List<SelectItem> pages = new ArrayList<SelectItem>();

    public MyBean() {
        for (int i = 1; i <= 10; i++) {
            pages.add(new SelectItem((i == 1) ? i : (i * 10)));
        }
    }

    public void changePage(ValueChangeEvent event) {
        if (event.getPhaseId() == PhaseId.INVOKE_APPLICATION) {
            setPage((Integer) event.getNewValue());
        } else {
            event.setPhaseId(PhaseId.INVOKE_APPLICATION);
            event.queue();
        }
    }

    public Integer getPage() {
        return page;
    }

    public List<SelectItem> getPages() {
        return pages;
    }

    public void setPage(Integer page) {
        this.page = page;
    }

}

(by the way, you used String instead of Integer which is imho the wrong type for numerical values, but here it works fine with String as well)

JSF page:

<h:form>
    <h:selectOneMenu value="#{myBean.page}" onchange="submit()"
        valueChangeListener="#{myBean.changePage}">
        <f:selectItems value="#{myBean.pages}" />
    </h:selectOneMenu>
</h:form>

I used Mojarra 1.2_13 on Tomcat 6.0.20.

By the way, the valueChangeListener is entirely superfluous. It works as fine without it. JSF just sets the selected page during Update Model Values phase.

BalusC
@BalusC: No I don't have <navigation-case> or <redirect/> directive defined in my faces-config.xml at all. When the onchange() method is called, this method grabs the selected page (plus 3 other selected values on the form) and pass all these back to the database for data retrieval. If I select page 2, the database should return record 11 to 20 as I'm limiting 10 records per page. The selectOneMenu should display page "2" after the form is submitted. Your pagination method in your blog is different to my requirement so I couldn't adopt it. Is the JS suggestion above the only solution?...
icepax
Oops! Apology for my typo above. The method above should read: onchange="pageChanged(), where pagechanged is a JavaScript function that's equal to: document.getElementById("Form1:btnSubmit").click();. The function behind btnSubmit is what I've described in my previous comment. I'm grateful to you or anyone else who can help me resolve this problem.
icepax
Can I add a h:inputHidden field to store the changed page value so when the form is submitted, I can retrieve the value from the hidden field. If this is possible, how can I define the getter/setter methods in a backing bean. I'm new to JSF so I really don't know how to do it this way. If you know, can you please provide some sample codes.
icepax
@BalusC: Yes, after submit, the selectOneMenu is re-populated with the correct list of pages- but the previously selected page is not selected after submit ie. the first item on the list is re-selected. Also, in my getSelectedPage() method above, if the returned page value is null, then it's default to "1" (as I can't have page null). I think this is what happened when the form is re-submitted. Is it because I'm re-populating the pages with the getSelectPages() method? I wonder if that's why it's losing the previous selected page value during the JSF phases.
icepax
Run a debugger. I don't see a problem in the as far posted information. Something else is colliding with this.
BalusC
A: 

@BalusC: I'll configure the debugger to see if I can see anything. I'm new to Eclipse and JSF so it might take me a while to figure this out. Anyway, I'll post what I find back here. Thank you so much for helping me. Much appreciated.

icepax
A: 

The debugger shows that the list is being reset to null each time due to the fact that I dynamically created the page count value for the backing bean from another class called DAOCounter (you're right about colliding). The revised backing bean is now:

public class PageBean
{
 private List<SelectItem> pages = new ArrayList<SelectItem>();
 private Integer page = 0;
 private Integer rows = 0;
 private Integer value;

 public void createPages() 
 {
        for (int i = 1; i <= page; i++) 
        {
         value = i == 1? 0: i * 10;
         pages.add(new SelectItem(value, Integer.toString(i)));
        } 
 }

 // getter methods
 public int getRows() {
  return rows;
 }

 public List<SelectItem> getPages() 
 {
  if (pages.size() == 0) {
   createPages();
  }

  return pages;
 }

 public Integer getPage() {
        return page;
    }

 public boolean getRender()
 {
  boolean rendered = page > 0? true: false;
  return rendered;
 }  

    // setter methods
 public void setRows(int rows) {  
  this.rows = rows;
 } 

    public void setPages(List<SelectItem> pages) {
        this.pages = pages;
    }

    public void setPage(Integer page) {
        this.page = page;
    }

    // action methods
    public void changePage(ValueChangeEvent event)
    {
        if (event.getPhaseId() == PhaseId.INVOKE_APPLICATION) {
         setPage((Integer) event.getNewValue());
        } else {
            event.setPhaseId(PhaseId.INVOKE_APPLICATION);
            event.queue();
        }
    }    
}

And the code for the counter class is:

public class DAOCounter 
{
 public static void setRows(Integer rows)
 {
  PageBean bean = (PageBean) FacesUtil.evaluateExpressionGet("#{pageBean}", PageBean.class);
  bean.setRows(rows);
 }

 public static void setPages(Integer pages)
 {
  PageBean bean = (PageBean) FacesUtil.evaluateExpressionGet("#{pageBean}", PageBean.class);
  bean.setPage(pages);
 }
}

And the layout:

<h:selectOneMenu id="page" value="#{pageBean.page}"
   valueChangeListener="#{pageBean.changePage}" onchange="pageChanged()">
   <f:selectItems value="#{pageBean.pages}" />
</h:selectOneMenu>

And the submit method:

Object bean = FacesUtil.getRequestMap("pageBean");
selectedPage = ((PageBean) bean).getPage(); // always returns the first item in the list
System.out.println("submit: "+selectedPage);

I'm not sure at this point how to retrieve the page number once it's selected and passed it to the submit method. Really stuck!...

Sorry for posting this as an answer - but when I tried to post as a comment, I don't have the option to format the codes display plus I only have a limit number of characters to type.

icepax
I am not sure, but the code seems unnecessarily overcomplicated. It look like that you want to take over all the JSF work in your own hands. Do you understand how the JSF lifecycle fits in each other? You may find this article an interesting read: http://balusc.blogspot.com/2006/09/debug-jsf-lifecycle.html
BalusC
A very constructive blog article. Thank you, BalusC. The only reason I'm doing the above is because the page total counter changes depends on what the user search criteria. One search result could have 200 records; while other may only have 5. Depending on the results, I construct the h:selectOneMenu based on the result. Since PageBean is commonly used by three forms, I made it generic enough so I can pass in the page total and create the h:selectOneMenu on the fly. I've done this part. But I haven't been able to pick up the selected page and re-populate form with the next set of records.
icepax
I notice you describe the use of "binding" in your blog. I may investigate this method and see if it can help me. Might have to google for some example of "HtmlSelectOneMenu" to try and understand how to use it. Anyway, thanks heap for all your help so far. You're a very knowledgable person...
icepax