【问题标题】:JSF 2: Using enums in the rendered attributeJSF 2:在渲染属性中使用枚举
【发布时间】:2011-06-10 01:18:30
【问题描述】:

有没有办法以声明方式检查枚举是否具有指定值。例如:

<h:graphicImage name="error.png" library="images" 
  rendered="#{viewController.current.status == Status.ERROR}" />

在托管 bean 中定义一个检查每个枚举值的方法有点乏味,例如

public boolean isStateIsError() {
  return current.getStatus() == Status.ERROR;
}

有没有更短/更好的方法?

【问题讨论】:

    标签: jsf enums jsf-2 el


    【解决方案1】:

    Until EL 3.0 不能在 EL 范围内导入枚举。但是,您可以像字符串一样对待和比较它们,即枚举常量值必须像下面这样引用。

    <h:graphicImage name="error.png" library="images" 
      rendered="#{viewController.current.status eq 'ERROR'}" />
    

    【讨论】:

    • 字符串比较的求值方式类似于调用equals()方法还是通过引用进行比较?
    【解决方案2】:

    我知道这个问题现在有点老了,但我遇到了同样的问题并找到了另一个解决方案,我想分享一下:

    创建一个自定义 EL-Resolver 并在 jsf 中使用枚举和 java 常量作为对象 el:

    <h:graphicImage name="error.png" library="images"  
          rendered="#{viewController.current.status == Status.ERROR}" />
    

    但在您可以通过这种方式使用枚举之前,您必须执行 3 个步骤。

    1.步骤 - 复制该类并通过您的 enumClass 替换“MY_ENUM”(在上面的示例中为“Status”)

    public class EnumCache {
        private Map<String, Object>  propertCache = new HashMap<String, Object>();
        private Map<String, Class>  baseCache = new HashMap<String, Class>();
        private static EnumCache staticEnumCache = null;
    
        public static EnumCache instance() {
            if (staticEnumCache == null) { staticEnumCache = new EnumCache(); }
            return staticEnumCache;
        }
        private EnumCache() {
            List<Class<?>> classes = new ArrayList<Class<?>>();
            classes.add(MY_ENUM.class);
    
            for(Class clazz : classes) {
                try {
                    baseCache.put(clazz.getSimpleName(), clazz);
                    Method m = clazz.getMethod("values", (Class[]) null);
                    Enum<?>[] valueList = (Enum[]) m.invoke(null, (Object[]) null);
                    for (Enum<?> en : valueList) {
                        propertCache.put(clazz.getSimpleName() + "." + en.name(), en);
                    }
                } catch (Exception e) {
                    System.err.println(clazz.getSimpleName(), e);
                }
            }
        }
        public Object getValueForKey(String key)  {
            return propertCache.get(key);
        }
        public Class getClassForKey(String key) {
            return baseCache.get(key);
        }
    }
    

    2。步骤 - 添加此 EnumResolver - 该类会将您的 JSF 表达式映射到缓存中的枚举(步骤 1)

    public class MyEnumResolver extends ELResolver {
    
        public Object getValue(ELContext context, Object base, Object property) {
            Object result = null;
            if (base == null) {
                result = EnumCache.instance().getClassForKey(property + "");
            } else if (base instanceof Class) {
                result = EnumCache.instance().getValueForKey(((Class) base).getSimpleName() + "." + property);
            }
            if (result != null) {
                context.setPropertyResolved(true);
            }
            return result;
        }
    
        public Class<?> getCommonPropertyType(ELContext context, Object base) {
            return null;
        }
        public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
            return null;
        }
        public Class<?> getType(ELContext context, Object base, Object property) {
            return null;
        }
        public boolean isReadOnly(ELContext context, Object base, Object property) {
            return false;
        }
        public void setValue(ELContext context, Object base, Object property, Object arg3) {
        }
    }
    

    3. step - 在 faces-config.xml 中注册 EnumResolver

    <faces-config>
        <application>
            <el-resolver>com.asd.MyEnumResolver</el-resolver>
        </application>
    </faces-config>
    

    注意: 如果你想以这种方式访问​​你的 java 常量,你只需要扩展 enumCache 类的构造函数。 这个(untestet)示例应该可以工作:

    baseCache.put(CLASS_WITH_CONSTANTS.getSimpleName(), clazz);
    for (Field field : CLASS_WITH_CONSTANTS.getDeclaredFields()) {
        try {
            propertCache.put(CLASS_WITH_CONSTANTS.getSimpleName() + "." 
                      + field.getName(), field.get(null));
        } catch (Exception e) { }
    }
    

    希望这个精简但有效的代码可以帮助任何人。


    更新

    我看到了这些好处:

    1. 如果您在 jsf 中使用字符串 (viewController.current.status == 'ERROR_abcdefg'),您可能会拼错该值,并且无法快速识别它。 使用我的解决方案,您在加载 jsf 文件时会出错,因为无法解析枚举。

    2. 您可以在源代码中看到“ERROR”是枚举“STATUS”的值。

    3. 比较 el 中的两个值时,也会比较枚举的类。 因此,例如 PersonState.ACTIV 与 AccounState.ACTIV 不同。

    4. 当我必须将枚举值从 PersonState.ACTIV 更改为 PersonState.ACTIVATED 时,我可以在我的源代码中搜索字符串“PersonState.ACTIV”。搜索“ACTIV”会有更多匹配项。

    【讨论】:

    • 这种方法究竟能给您带来什么好处?在 Java 中,使用直接枚举引用的主要优点是在编译时强制执行有效值,但这种优势在 EL 中不存在,在此解析器中也不存在。
    • 这比在托管 bean 中创建方法还要乏味!不过,很高兴知道这是可以做到的。
    【解决方案3】:

    我通过statically 将所有枚举键(在渲染的 UI 组件中使用)转储到地图中解决了类似的问题,然后我使用静态 getByKey 方法将 UI 中的值转换为实际值setter 中的原生枚举,如果提供的值无效,则抛出异常:

    public enum ReportType {
    
        FILING("F", "Filings"),
        RESOLUTION("R", "Resolutions"),
        BASIS("B", "Bases"),
        STAFF("T", "Staff Counts"),
        COUNTS("I", "Counts");
    
        private String key;
        private String label;
    
        private static Map<String, ReportType> keyMap = new HashMap<String, ReportType>();  
    
        static {
            for(ReportType type : ReportType.values()) {
                keyMap.put(type.getKey(), type);
            }
        }
    
        private ReportType(String _key, String _label) {
            this.key = _key;
            this.label = _label;
    
        }
    
        public String getKey() {
            return this.key;
        }
    
        public String getLabel() {
            return this.label;
        }
    
        public static List<ReportType> getValueList() {
            return Arrays.asList(ReportType.values());
        }
    
        public static ReportType getByKey(String _key) {
            ReportType result = keyMap.get(_key);
    
            if(result == null) {
                throw new IllegalArgumentException("Invalid report type key: " + _key);
            }
    
            return result;
        }
    }
    

    在UI层,枚举键作为值,枚举标签作为标签:

    <f:selectItems var="rptTypeItem" value="#{reportController.allReportTypes}" 
        itemLabel="#{rptTypeItem.label}" itemValue="#{rptTypeItem.key}"/>
    

    managed bean 中,我使用枚举中的getValueList() 将枚举转换为可渲染列表:

    public List<ReportType> getAllReportTypes() {
        return ReportType.getValueList();
    }
    

    最后,托管 bean 中的 [g|s]setter 如下所示:

    public String getReportType() {
        return this.crtRptType.getKey();
    }
    
    public void setReportType(String _val) {
        this.crtRptType = ReportType.getByKey(_val);
    }
    

    【讨论】:

      【解决方案4】:

      我认为可以通过以下方式完成:

      在你的 bean 中创建一个返回枚举列表的方法,例如

      public Status[] getStatuses() {
        Status.values();
      }
      

      那么你可以像这样在EL中使用枚举

      <h:graphicImage name="error.png" library="images" 
        rendered="#{viewController.current.status == someBean.statuses[0]}" />
      

      假设枚举成员的顺序不会改变(例如,这里的 statuses[0] 是 ERROR)。但是,我会像这样固定位置:

      public Status[] getStatuses() {
        Status myStatuses = new Status [2]; // or whatever number of statuses you are going to use in UI
        myStatuses [0] = Status.ERROR;
        myStatuses [1] = Status.RUNNING;
        return myStatuses;
      }
      

      这仍然不是动态解决方案,但它比 EL 中的硬编码要好。当您为状态使用本地化时可能特别有用(枚举值取决于语言环境/翻译)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-09-09
        • 1970-01-01
        • 2013-01-27
        • 2014-02-27
        • 2023-03-19
        • 2011-08-18
        • 1970-01-01
        • 2011-03-09
        相关资源
        最近更新 更多