1

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

feder
  • 1,705
  • 5
  • 23
  • 36
  • have you tried using `` instead of the `` ? – Daniel Sep 01 '13 at 11:00
  • Nope. Tried that before. Makes no difference. Same behaviour: walks through all the JSF faces, but does NOT trigger the setter method. (thanks anyway for your feedback) – feder Sep 01 '13 at 11:21
  • 1
    do you have any messages in the server log , i mean , try adding id to that `h:message` , like this: `` and render it with the `` – Daniel Sep 01 '13 at 11:25
  • Remove `@ManagedBean(name = "pubCategoryConverterController")` annotation. `@FacesConverter` is enough and reference it as `converter="pubCategoryConverter"`. Your converter is probably failing when trying to get the selection as an object, so debug it and see what happens. – Xtreme Biker Sep 01 '13 at 14:39
  • @XtremeBiker If I remove ´@ManagedBean´, the ´@Inject´ or ´@EJB´ annotation does NOT inject the beans. – feder Sep 01 '13 at 17:56
  • I don't use EJB, but `@ManagedBean` and `@FacesConverter` annotations [are superfluous](http://stackoverflow.com/a/8919591/1199132) when putting them together. You can maintain `@ManagedBean`, but you'll need to access it as `converter="#{pubCategoryConverter}"` and then remove the converter annotation. – Xtreme Biker Sep 01 '13 at 20:46

2 Answers2

3

The converter is called in the "Process Validations" phase of the JSF lifecycle whereas the setter is called later, during the "Update Model Values" phase. Each phase goes through the entire page which means a validation error in any component will prevent all model updates. If you're not seeing validation errors on the page try checking your message tags.

The skipping of lifecycle phases is done by calling FacesContext.renderResponse(). See UIInput.executeValidate() and LifeCycleImpl.execute() for details.

2

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);
feder
  • 1,705
  • 5
  • 23
  • 36