eventsjsf-2valuechangelistener

how to use value change listener


I don't know how to deal with f:valueChangeListener , I want to select country and the capital appear so this is my code but it doen't work what is miss or what is the wrong?

 Country:
  <h:selectOneMenu value="#{event.country}" onchange="submit()">
        <f:valueChangeListener type="org.jsf.model.ValueListener"/>
        <f:selectItems value="#{event.countries}"/>     
  </h:selectOneMenu>
 Capital: #{event.capital}  

My Managed bean

public class EventsBean{

private String capital;
private String country;
String countryCapital;


private String [] countries = {"Select","Egypt","United States","Kuwait"};

public String[] getCountries() {
    return countries;
  }

// getters and setters
}

The class that implements ValueChangeListener

package org.jsf.model;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ValueChangeEvent;
import javax.faces.event.ValueChangeListener;

public class ValueChangeClass implements ValueChangeListener {
String capital;
 @Override
   public void processValueChange(ValueChangeEvent event)throws AbortProcessingException {

if ("Egypt".equals(event.getNewValue())                  capital = "Cairo";
else if ("Kuwait".equals(event.getNewValue()))           capital = "Kuwait";
else if ("United States".equals(event.getNewValue()))    capital = "Washington";
else   capital = "";


new EventsBean().setCapital(capital);
}
}

It doesn't work ! Is this new EventsBean().setCapital(capital);right ?


Solution

  • Is this new EventsBean().setCapital(capital);right ?

    No, it is not right. You're manually creating a brand new instance instead of using the one which is managed by JSF. Your instance would totally disappear once the method finishes and returns. You should instead be setting the capital in the instance which is managed by JSF. There are several ways to achieve this. If you really intend to use the ValueChangeListener this way (which is rather strange for this particular purpose by the way), then you need to fix it as follows:

    FacesContext context = FacesContext.getCurrentInstance();
    EventsBean eventsBean = context.getApplication().evaluateExpressionGet(context, "#{event}", EventsBean.class);
    eventsBean.setCapital(capital);
    

    Easier would be to do the job just in the EventsBean itself.

    <h:selectOneMenu value="#{event.country}" valueChangeListener="#{event.changeCountry}" onchange="submit()">
        <f:selectItems value="#{event.countries}"/>     
    </h:selectOneMenu>
    Capital: #{event.capital}
    
    private String country;
    private String capital;
    private Map<String, String> capitals;
    
    @PostConstruct
    public void init() {
        capitals = new HashMap<>();
        capitals.put("Egypt", "Cairo");
        capitals.put("Kuwait", "Kuwait");
        capitals.put("United States", "Washington D.C.");
    }
    
    public void changeCountry(ValueChangeEvent event) {
        capital = capitals.get(event.getNewValue());
    }
    

    Or, since you're already using JSF 2.0, much better is to use <f:ajax>. It does the right job at the right moment. (Ab)using the valueChangeListener the way as in your original code is actually a leftover of the JSF 1.x era.

    <h:selectOneMenu value="#{event.country}">
        <f:selectItems value="#{event.countries}"/>     
        <f:ajax listener="#{event.changeCountry}" render="capital" />
    </h:selectOneMenu>
    Capital: <h:outputText id="capital" value="#{event.capital}" />
    
    // ...
    
    public void changeCountry() {
        capital = capitals.get(country);
    }
    

    See also: