Pages

Wednesday, March 11, 2009

Use OGNL expressions to fill your SelectOneMenu

JSF selectOneMenu component is loaded with "selectItems" tag. Like; 
<h:selectOneMenu id="som_city" value="#{valueObject.city}">
<t:selectItems value="#{cityService.getCities}" itemLabel="#{val.name}" itemValue="#{val.this}" var="val" />
</h:selectOneMenu>
With the help of some converter we are able to list cities in the combo with the expression "#{cityService.getCities}". But often what we realy want to do is list the cities of certain country which is perhaps choosen from another combo. What we need is another expression like "#{cityService.getCities(someCountry)}". 
To achive this I have overriden the myfaces UISelectItems and gave it the power of Ognl expressions:
import java.util.AbstractMap;
import java.util.HashSet;
import java.util.Set;

import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;

import ognl.Ognl;
import ognl.OgnlException;

import org.apache.myfaces.custom.selectitems.UISelectItems;

public class UISelectSmarterItems extends UISelectItems {

private String expString;

public String getExpString() {
if (expString != null)
return expString;

ValueBinding vb = getValueBinding("expString");
String v = vb != null ? (String) vb.getValue(getFacesContext()) : null;
return v;
}

public void setExpString(String expString) {
this.expString = expString;
}

@Override
public Object getValue() {
String str = getExpString();
if (str != null && str.length() > 0) {
setValue(evaluateExpression(str));
}

return super.getValue();
}

private Object evaluateExpression(String aExp) {
try {
return Ognl.getValue(aExp, new FacesEvaluationContext());
}
catch (OgnlException e) {
throw new RuntimeException(aExp);
}
}

private final class FacesEvaluationContext extends AbstractMap {

@Override
public Object get(Object key) {
return resolveVariable(key);
}

private Object resolveVariable(Object key) {
return getFacesContext().getApplication().createValueBinding("#{" + key + "}").getValue(getFacesContext());
}

@Override
public Set entrySet() {
return new HashSet();
}
}

@Override
public Object saveState(FacesContext context) {
Object values[] = new Object[2];
values[0] = super.saveState(context);
values[1] = expString;
return ((Object) (values));
}

@Override
public void restoreState(FacesContext context, Object state) {
Object values[] = (Object[]) state;
super.restoreState(context, values[0]);
expString = (String) values[1];
}

}
What I did is add expString property and give it the power of Ognl expression. In the end we are able write this;
<injsf:selectItems expString="cityService.getCities(someCity)"
var="val" itemLabel="#{val.name}" itemValue="#{val.this}"
/>

No comments:

Post a Comment