Search code examples
jsf-2richfaces

Setter of h:selectOneMenu is not invoked


I just tried to change a value of a select input box. Loading the page runs into my breakpoint for the getter method of the pubcategory property. Good so far. Changing the value, does NOT invoke the setter method. I trigger an Richfaces ajax processing. I confirm, that all JSF phases are walked through (I also see the JPA-SQL select queries, where I would expect an update statement for changing the value - well, can't be, if the setter method is not triggered). This is my selectOneMenu code

<h:selectOneMenu id="pubCategoryId" converter="#{pubCategoryConverter}" value="#{pubController.pubCategory}">                           
<f:selectItems value="#{listPubCategoryController.pubCategories}" var="category" itemLabel="#{category.name}" itemValue="#{category}" />
<a4j:ajax event="change" execute="@this" />
</h:selectOneMenu>
<h:message for="pubCategoryId" style="color:red" />

My converter is invoked on both times. the getAsString method, when I load the page and the getAsObject when the on-change action is triggered. From this I concluse, the change really goes back to the server. But - again - it never triggers the setter method.

@ManagedBean(name = "pubCategoryConverterController")
@FacesConverter(value = "pubCategoryConverter")
//@Named 
public class PubCategoryConverter implements Converter {
@Inject
private PubCategoryRepository pubCategoryRepository;

public PubCategoryConverter() {
}

// from page to backing bean
@Override
public Object getAsObject(FacesContext ctx, UIComponent component,
        String value) {

    PubCategory pubCat = pubCategoryRepository.getPubCategoryById(new Long(
            value));
    return pubCat;
}

// from backing bean to page
@Override
public String getAsString(FacesContext fc, UIComponent uic, Object o) {
    PubCategory pubCat = ((PubCategory) o);
    return pubCat.getId().toString();
}
}

Same story if I annotate the converter with @Named instead of @FacesConverter/@ManagedBean. Any clue or hints anyone?

Using JBoss 7.1.1, Richfaces 4.3.3, JSF 2.0


Solution

  • I hooked up the phase listener and printed the event.getFacesContext().getMessageList(). And, there is an error although not printed to the <h:Message for="pubCategoryId"/>. The error is msg j_idt18:pubFormE:pubCategoryId: Validation Error: Value is not valid

    package com.foo;
    
    import java.util.List;
    import java.util.logging.Logger;
    import javax.faces.event.PhaseEvent;
    import javax.faces.event.PhaseListener;
    import javax.faces.application.FacesMessage;
    import javax.faces.context.FacesContext;
    
    public class PhaseTracker implements PhaseListener {
    
        private static final Logger logger = Logger.getLogger("org.exadel.helper");
    
        public void beforePhase(PhaseEvent e) {
            List<FacesMessage> msgs = e.getFacesContext().getMessageList();
            for (FacesMessage msg : msgs) {
                logger.info("before msg " + msg.getSummary() + " :: " + msg.getDetail());
            }
            logger.info("BEFORE " + e.getPhaseId());
        }
    
        public void afterPhase(PhaseEvent e) {
            logger.info("AFTER " + e.getPhaseId());
        }
    }
    

    The issue is with the equals(Object o) method within of the model object (some call that DTO). As stated in many forums, the values that you compare within this method must not look like e.g. this.id == o.id, because it compares instances not the inner state. Use equals instead this.id.equals(o.id). Once that is fixed the error Value is not valid will go away.

    After all, I noticed the following. If you want to use the selectOneMenu with the tag attribute converter instead of <f:converter ../>, e.g. ...

    <h:selectOneMenu value="#{pubController.pubCategory}" converter="#{pubCategoryConverterController}">
    

    ... you need to annotate your converter also as a @ManagedBean instance in addition to @FacesConverter, e.g.

    @ManagedBean(name="pubCategoryConverterController")
    @FacesConverter(value = "pubCategoryConverter")
    

    On the other hand, if you want to use <f:converter converterId="pubCategoryConverter"> tag, you need to reference the faces converter name - NOT an instance of a managed bean. Notice, there is no EL #{...} around the converter name. However, in this case, you CANNOT inject a bean into your converter. As a consequence, in your converter bean, you need to instantiate a controller from the application context in order to use EJB services. e.g.

    PubCategoryController pubCategoryController = ctx.getApplication().evaluateExpressionGet(ctx, "#{pubCategoryController}", PubCategoryController.class);