2
votes

I am attempting to create a JSF page where the user selects values in two selectOneMenu components. The values in these selections will then be passed to a SQL query to update a table displayed on the page.

I've bound each of the selection items to an appropriate variable in the backing bean, but after changing the value selected in the selectOneMenu, the bound property in the backing bean does not change, and the query is executed but doesn't return any results. (As an aside, the getters also appear to be called twice when the page is initially displayed).

I cannot use any other third party components (such as RichFaces, etc.) for this implementation.

Any insight into this is appreciated!

Here is the code:

notes.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
    <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
      <title></title>
 </head>
<body>

<f:view>

<h:form id="notelistform">
    <f:subview id="loginbar">
        <jsp:include page="/jsp/loginbar.jsp" />
    </f:subview>

    <h:outputText value="Worktype:" />
    <h:selectOneMenu value="#{pc_noteListBacker.worktype}"
        title="worktype" id="worktypeList" onchange="submit()">
        <f:selectItems value="#{pc_noteListBacker.selectWorktypes}" />
        <f:valueChangeListener
            type="<package>.web.backer.note.NoteListBacker" />

    </h:selectOneMenu>

    <h:outputText value="Product:" />
    <h:selectOneMenu value="#{pc_noteListBacker.product}" title="product"
        id="productList" onchange="submit()">
        <f:selectItems value="#{pc_noteListBacker.selectProducts}" />
        <f:valueChangeListener
            type="<package>.web.backer.note.NoteListBacker" />
    </h:selectOneMenu>


    <h:dataTable id="notelisttable" value="#{pc_noteListBacker.noteList}"
        var="item" bgcolor="#F1F1F1" border="10" cellpadding="5"
        cellspacing="3" first="0" width="80%" dir="LTR" frame="hsides"
        rules="all" summary="Status and notes note table display."
        binding="#{pc_noteListBacker.noteListTable}">


        <h:column>
            <f:facet name="header">
                <h:outputText value="Note ID" />
            </f:facet>
            <h:outputText value="#{item.noteId}"></h:outputText>
        </h:column>


        <h:column>
            <f:facet name="header">
                <h:outputText value="Note Category" />
            </f:facet>
            <h:outputText value="#{item.noteCategory}"></h:outputText>
            <f:facet name="footer">
                <h:commandButton value="Add" action="#{pc_noteListBacker.insert}">
                </h:commandButton>
            </f:facet>
        </h:column>

        <h:column>
            <f:facet name="header">
                <h:outputText value="Note Subcategory" />
            </f:facet>
            <h:outputText value="#{item.noteSubCategory}"></h:outputText>
        </h:column>


        <h:column>
            <f:facet name="header">
                <h:outputText value="Note" />
            </f:facet>
            <h:outputText value="#{item.note}"></h:outputText>
        </h:column>



        <h:column>
            <h:commandButton value="Edit" action="#{pc_noteListBacker.edit}">
            </h:commandButton>
        </h:column>
        <h:column>
            <h:commandButton value="Delete" action="#{pc_noteListBacker.delete}"></h:commandButton>
        </h:column>
    </h:dataTable>

</h:form>

NoteListBacker.java

 package <package>.web.backer.note;

 import java.util.List;
 import java.util.ArrayList;

 import javax.faces.component.UIComponent;
 import javax.faces.component.UIComponentBase;
 import javax.faces.component.UIData;
 import javax.faces.context.FacesContext;
 import javax.faces.event.AbortProcessingException;
 import javax.faces.event.ValueChangeEvent;
 import javax.faces.event.ValueChangeListener;
 import javax.faces.model.ListDataModel;
 import javax.faces.model.SelectItem;
 import javax.swing.JOptionPane;


 public class NoteListBacker implements ValueChangeListener {

private UIData noteListTable;

private Note note;

private NoteCategory noteCategory;

private NoteSubCategory noteSubCategory;

private NoteDao nDao;

private SybaseSqlDao sybaseDao;

private List<Note> noteList;

private List<SelectItem> selectWorktypes;

private List<SelectItem> selectProducts;

private String worktype;

private List<String> worktypeList;

private String product;

private List<Product> productList;

private int noteId;

public NoteListBacker() {

    nDao = new NoteDao();
    sybaseDao = new SybaseSqlDao();
    selectWorktypes = new ArrayList<SelectItem>();
    selectProducts = new ArrayList<SelectItem>();
    noteList = new ArrayList<Note>();
    note = new Note();
    worktypeList = new ArrayList<String>();
    productList = new ArrayList<Product>();
}

public String insert() {
    return "success";
}

public String save() {
    return "notes";
}

public String edit() {
    return "success";
}

public String update() {
    return "notes";
}

public String delete() {
    Note delNote = (Note) noteListTable.getRowData();
    nDao.deleteNote(delNote);
    return "notes";
}

public String cancel() {
    return "notes";
}

public Note getNote() {
    return note;
}

public void setNote(Note note) {
    this.note = note;
}

public NoteDao getNDao() {
    return nDao;
}

public UIData getNoteListTable() {
    return noteListTable;
}

public void setNoteListTable(UIData noteListTable) {
    this.noteListTable = noteListTable;
}

public List<Note> getNoteList() {


    noteList = nDao.selectNotes(worktype, product); //
    return noteList;
}

public void setNoteList(List<Note> noteList) {
    this.noteList = noteList;
}

public void setNDao(NoteDao dao) {
    nDao = dao;
}

public String getProduct() {
    return product;
}

public void setProduct(String product) {
    this.product = product;
}

public String getWorktype() {
    return worktype;
}

public void setWorktype(String worktype) {
    this.worktype = worktype;
}

public NoteCategory getNoteCategory() {
    return noteCategory;
}

public void setNoteCategory(NoteCategory noteCategory) {
    this.noteCategory = noteCategory;
}

public NoteSubCategory getNoteSubCategory() {
    return noteSubCategory;
}

public void setNoteSubCategory(NoteSubCategory noteSubCategory) {
    this.noteSubCategory = noteSubCategory;
}

public int getNoteId() {
    return noteId;
}

public void setNoteId(int noteId) {
    this.noteId = noteId;
}

public SybaseSqlDao getSybaseDao() {
    return sybaseDao;
}

public void setSybaseDao(SybaseSqlDao sybaseDao) {
    this.sybaseDao = sybaseDao;
}

public List<SelectItem> getSelectWorktypes() {
    selectWorktypes.clear();

    worktypeList = this.getWorktypeList();

    for (String strt : worktypeList) {

        SelectItem si = new SelectItem();
        si.setValue(strt);
        si.setLabel(strt);
        si.setDescription(strt);
        selectWorktypes.add(si);
    }

    return selectWorktypes;
}

public void setSelectWorktypes(List<SelectItem> selectWorktypes) {

    this.selectWorktypes = selectWorktypes;
}

public List<String> getWorktypeList() {

    worktypeList = sybaseDao.selectWorktypes();
    return worktypeList;
}

public void setWorktypeList(List<String> worktypeList) {
    this.worktypeList = worktypeList;
}

public List<Product> getProductList() {

    productList = sybaseDao.selectProducts();
    return productList;
}

public void setProductList(List<Product> productList) {
    this.productList = productList;
}

public List<SelectItem> getSelectProducts() {
    selectProducts.clear();

    productList = this.getProductList();

    for (Product prod : productList) {

        SelectItem si = new SelectItem();
        si.setValue(prod);
        si.setLabel(prod.toString());
        si.setDescription(prod.toString());
        selectProducts.add(si);
    }

    return selectProducts;
}

public void setSelectProducts(List<SelectItem> selectProducts) {
    this.selectProducts = selectProducts;
}

public void processValueChange(ValueChangeEvent arg0) {

    if (arg0.getComponent().getId().equalsIgnoreCase("productList")) {
        this.setProduct(arg0.getNewValue().toString());

    }
    if (arg0.getComponent().getId().equalsIgnoreCase("worktypeList")) {
        this.setWorktype(arg0.getNewValue().toString());
    }

}

 }

faces-config.xml

 <?xml version="1.0"?>

 <!DOCTYPE faces-config PUBLIC
   "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"
   "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">

 <faces-config>

<lifecycle>
    <phase-listener>
        <package>.utils.NoCachePhaseListener
    </phase-listener>
</lifecycle>

<managed-bean>
    <managed-bean-name>pc_userBacker</managed-bean-name>
    <managed-bean-class>
        <package>.web.backer.UserBacker
    </managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

<managed-bean>
    <managed-bean-name>pc_noteCategoryListBacker</managed-bean-name>
    <managed-bean-class>
        <package>.web.backer.note.NoteCategoryListBacker
    </managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
</managed-bean>

<managed-bean>
    <managed-bean-name>
        pc_noteSubCategoryListBacker
    </managed-bean-name>
    <managed-bean-class>
        <package>.web.backer.note.NoteSubCategoryListBacker
    </managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
</managed-bean>

<managed-bean>
    <managed-bean-name>pc_noteListBacker</managed-bean-name>
    <managed-bean-class>
        <package>.web.backer.note.NoteListBacker
    </managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

<managed-bean>
    <managed-bean-name>pc_statusListBacker</managed-bean-name>
    <managed-bean-class>
        <package>.web.backer.status.StatusListBacker
    </managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
</managed-bean>

<managed-bean>
    <managed-bean-name>pc_statusReasonBacker</managed-bean-name>
    <managed-bean-class>
        <package>.web.backer.status.StatusReasonBacker
    </managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
</managed-bean>


<!-- Notes -->
<navigation-rule>
    <from-view-id>/jsp/notes/notes.jsp</from-view-id>
    <navigation-case>
        <from-action>#{pc_noteListBacker.edit}</from-action>
        <from-outcome>success</from-outcome>
        <to-view-id>/jsp/notes/noteedit.jsp</to-view-id>
    </navigation-case>
    <navigation-case>
        <from-action>#{pc_noteListBacker.insert}</from-action>
        <from-outcome>success</from-outcome>
        <to-view-id>/jsp/notes/notenew.jsp</to-view-id>
    </navigation-case>


</navigation-rule>


<!-- Note Categories -->
<navigation-rule>
    <from-view-id>/jsp/notes/notecategories.jsp</from-view-id>
    <navigation-case>
        <from-action>#{pc_noteCategoryListBacker.edit}</from-action>
        <from-outcome>success</from-outcome>
        <to-view-id>/jsp/notes/notecategoryedit.jsp</to-view-id>
    </navigation-case>
    <navigation-case>
        <from-action>
            #{pc_noteCategoryListBacker.insert}
        </from-action>
        <from-outcome>success</from-outcome>
        <to-view-id>/jsp/notes/notecategorynew.jsp</to-view-id>
    </navigation-case>


</navigation-rule>

<!-- Note Subcategories -->
<navigation-rule>
    <from-view-id>/jsp/notes/notesubcategories.jsp</from-view-id>
    <navigation-case>
        <from-action>
            #{pc_noteSubCategoryListBacker.edit}
        </from-action>
        <from-outcome>success</from-outcome>
        <to-view-id>/jsp/notes/notesubcategoryedit.jsp</to-view-id>
    </navigation-case>
    <navigation-case>
        <from-action>
            #{pc_noteSubCategoryListBacker.insert}
        </from-action>
        <from-outcome>success</from-outcome>
        <to-view-id>/jsp/notes/notesubcategorynew.jsp</to-view-id>
    </navigation-case>
</navigation-rule>

<!-- Statuses -->
<navigation-rule>
    <from-view-id>/jsp/statuses/statuses.jsp</from-view-id>
    <navigation-case>
        <from-action>#{pc_statusListBacker.edit}</from-action>
        <from-outcome>success</from-outcome>
        <to-view-id>/jsp/statuses/statusedit.jsp</to-view-id>
    </navigation-case>
    <navigation-case>
        <from-action>#{pc_statusListBacker.insert}</from-action>
        <from-outcome>success</from-outcome>
        <to-view-id>/jsp/statuses/statusnew.jsp</to-view-id>
    </navigation-case>
</navigation-rule>

<!-- Status Reasons -->
<navigation-rule>
    <from-view-id>/jsp/statuses/statusreasons.jsp</from-view-id>
    <navigation-case>
        <from-action>#{pc_statusReasonBacker.edit}</from-action>
        <from-outcome>success</from-outcome>
        <to-view-id>/jsp/statuses/statusreasonedit.jsp</to-view-id>
    </navigation-case>
    <navigation-case>
        <from-action>#{pc_statusReasonBacker.insert}</from-action>
        <from-outcome>success</from-outcome>
        <to-view-id>/jsp/statuses/statusreasonnew.jsp</to-view-id>
    </navigation-case>
</navigation-rule>

<!-- static and general actions-->
<!-- what page the action is coming from doesn't matter here -->
<navigation-rule>

    <navigation-case>
        <from-outcome>login</from-outcome>
        <to-view-id>/login.jsp</to-view-id>
    </navigation-case>

    <navigation-case>
        <from-outcome>menu</from-outcome>
        <to-view-id>/menu.jsp</to-view-id>
    </navigation-case>

    <!-- Notes -->
    <navigation-case>
        <from-outcome>notes</from-outcome>
        <to-view-id>/jsp/notes/notes.jsp</to-view-id>
    </navigation-case>


    <!-- Note Categories -->
    <navigation-case>
        <from-outcome>notecategories</from-outcome>
        <to-view-id>/jsp/notes/notecategories.jsp</to-view-id>
    </navigation-case>

    <!-- Note Subcategories -->
    <navigation-case>
        <from-outcome>notesubcategories</from-outcome>
        <to-view-id>/jsp/notes/notesubcategories.jsp</to-view-id>
    </navigation-case>

    <!-- Statuses -->
    <navigation-case>
        <from-outcome>statuses</from-outcome>
        <to-view-id>/jsp/statuses/statuses.jsp</to-view-id>
    </navigation-case>



    <!-- Status Reasons -->
    <navigation-case>
        <from-outcome>statusreasons</from-outcome>
        <to-view-id>/jsp/statuses/statusreasons.jsp</to-view-id>
    </navigation-case>


</navigation-rule>
 </faces-config>
2

2 Answers

1
votes

I've only glanced down the code, but I'd guess that part of the problem is probably here:

public void processValueChange(ValueChangeEvent arg0) {
        if (arg0.getComponent().getId().equalsIgnoreCase("productList")) {
                this.setProduct(arg0.getNewValue().toString());

        }
        if (arg0.getComponent().getId().equalsIgnoreCase("worktypeList")) {
                this.setWorktype(arg0.getNewValue().toString());
        }
}

If you read the doc for the valueChangeListener type attribute, it says this:

Fully qualified Java class name of a ValueChangeListener to be created and registered.

That means that you'll have at least three instances of <package>.web.backer.note.NoteListBacker - two created as listeners and a third created as a managed bean. The last one is the only one that the table is bound to and any state changes in the listeners are irrelevant.

To fix:

  1. Do not have NoteListBacker implement ValueChangeListener; there is no point.
  2. Either bind the selectOneMenu's valueChangeListener attribute to a #{pc_noteListBacker.valueChange} method (see the doc for details) OR have a separate ValueChangeListener implementation that looks up pc_noteListBacker directly from the session using the FacesContext.
1
votes

I ran into this pretty often, till I learned what is the problem here. When submitting the page JSF will run through the UPDATE_MODEL phase AFTER your call to processValueChange(). So the change to your BackingBean will be overwritten by the values in the HTTP request and take no effect.

To prevent this from happening, you will have to issue a navigation case manually in your ValueChangeListener. You do this by inserting the following line at the end of processValueChange().

FacesContext.getCurrentInstance().getApplication().getNavigationHandler().handleNavigation(
                FacesContext.getCurrentInstance(), null, "stay_here");

"stay_here" refers to a navigation case in your faces-config.xml

<!-- Notes -->
<navigation-rule>
        <from-view-id>/jsp/notes/notes.jsp</from-view-id>
        <navigation-case>
                <from-action>#{pc_noteListBacker.edit}</from-action>
                <from-outcome>success</from-outcome>
                <to-view-id>/jsp/notes/noteedit.jsp</to-view-id>
        </navigation-case>
        <navigation-case>
                <from-action>#{pc_noteListBacker.insert}</from-action>
                <from-outcome>success</from-outcome>
                <to-view-id>/jsp/notes/notenew.jsp</to-view-id>
        </navigation-case>
        <navigation-case>
                <from-outcome>stay_here</from-outcome>
                <to-view-id>/jsp/notes/notes.jsp</to-view-id>
        </navigation-case>

</navigation-rule>

Hope this helps.

Regards