(续)Ext 的 DateField 输入日期很方便,因此决定把它做成 JSF 组件来使用。
写一个 JSF 组件比较麻烦,至少要写Component 类和一个 Tag 类,这种情况下,Component 负责 render 工作。
但通常我们都会写多一个 Renderer,负责 encode 、decode 及 转换客户端提交的数值。
btw,Renderer 其实是通过 component 来调用的,因此,Renderer 中有什么方法,都可在UIComponent 中找到。
这个组件叫 ExtInputDate,Component 代码:
- public class ExtInputDate extends ExtInput {
- public final static String COMPONENT_TYPE = "example.ExtInputDate";
- public final static String COMPONENT_FAMILY = "example.ExtInputDate";
- public final static String DEFAULT_FORMAT = "yyyy-MM-dd";
- private String format;
- private Integer width;
- private Boolean readonly;
- private Boolean disabled;
- private String styleClass;
- @Override
- public String getFamily() {
- return COMPONENT_FAMILY;
- }
- @Override
- public String getEndingScript() {
- // 输出 JavaScript 代码,略。注意转换 Java 的日期格式为 Ext 的日期格式
- // ...
- }
- public String getFormat() {
- if (format == null) {
- ValueExpression ve = getValueExpression("format");
- if (ve != null) {
- format = (String) FacesUtils.getExpressionValue(ve, getFacesContext().getELContext());
- }
- if (format == null) {
- format = DEFAULT_FORMAT;
- }
- }
- return format;
- }
- public void setFormat(String format) {
- this.format = format;
- }
- // 其他 getter / setter, 略
- // TODO: ...
- @Override
- public void restoreState(FacesContext context, Object state) {
- Object values[] = (Object[]) state;
- super.restoreState(context, values[0]);
- format = (String)values[1];
- width = (Integer)values[2];
- readonly = (Boolean)values[3];
- disabled = (Boolean)values[4];
- styleClass = (String)values[5];
- }
- @Override
- public Object saveState(FacesContext context) {
- Object values[] = new Object[6];
- values[0] = super.saveState(context);
- values[1] = format;
- values[2] = width;
- values[3] = readonly;
- values[4] = disabled;
- values[5] = styleClass;
- return ((Object) (values));
- }
- }
Component 必须有 restoreState 和 saveState 方法,而且在这两个方法里面必须调用 super 的同名方法。
这一点,感觉很不爽...
UIInput 类的组件,有个变量叫 submittedValue,这个是客户端提交的值,
在 Apply Request Value 阶段 decode 的时候被设置,可以转换为String 类型;
另外有个 value 变量,存放 value 表达式的值。
那么,在 render 的时候,显示哪个值呢?
一般的做法是,如果 submittedValue 不为null,显示 submittedValue;否则显示 value。
Renderer类:
- public class ExtInputDateRenderer extends Renderer {
- public final static String RENDERER_TYPE = "example.ExtInputDateRenderer";
- @Override
- public void encodeBegin(FacesContext context, UIComponent component)
- throws IOException {
- if (component.isRendered()) {
- ResponseWriter writer = context.getResponseWriter();
- String clientId = component.getClientId(context);
- writer.startElement("div", component);
- writer.writeAttribute("id", clientId, "clientId");
- String styleClass = (String) component.getAttributes().get("styleClass");
- if (StringUtils.isNotEmpty(styleClass)) {
- writer.writeAttribute("class", styleClass, null);
- }
- writer.endElement("div");
- }
- }
- @Override
- public void decode(FacesContext context, UIComponent component) {
- if (component.isRendered() && component instanceof ExtInputDate) {
- Map paramMap = context.getExternalContext()
- .getRequestParameterMap();
- String clientId = component.getClientId(context);
- // 允许 readonly
- if (RendererUtils.isDisabled(component))
- return;
- if (paramMap.containsKey(clientId)) {
- ((EditableValueHolder) component).setSubmittedValue(paramMap.get(clientId));
- }
- } else {
- throw new IllegalArgumentException("Unsupported component class "
- + component.getClass().getName());
- }
- }
- @SuppressWarnings("unchecked")
- public Object getConvertedValue(FacesContext context,
- UIComponent component, Object submittedValue)
- throws ConverterException {
- ValueExpression valueExpression = component.getValueExpression("value");
- if (valueExpression == null)
- return null;
- Class converterType = valueExpression.getType(context.getELContext());
- if (converterType == String.class) {
- return submittedValue;
- } else if (converterType == Date.class
- || converterType == java.sql.Date.class) {
- String s = (String) submittedValue;
- if (StringUtils.isEmpty(s))
- return null;
- ExtInputDate inputDate = (ExtInputDate) component;
- SimpleDateFormat df = new SimpleDateFormat(inputDate.getFormat());
- try {
- return df.parse(s);
- } catch (ParseException e) {
- throw new ConverterException(e);
- }
- } else {
- throw new ConverterException();
- }
- }
- }
这个组件里面,decode 所做的事情只是设置 submittdValue,
getConvertedValue() 函数把客户端提交的值,转换为相应的类型。
这个函数在 Update Model Value阶段由 Component 调用,
更新 managed-bean 的值。
一般来说,需要考虑是否有 converter。
不过这个组件中,自己负责日期转换的工作,因此不需要 converter。
此外,这个组件只支持 String, java.util.Date, java.sql.Date 三种类型的值。