JSF 2: использование перечислений в атрибуте rendered
Есть ли способ проверить декларативно, имеет ли значение перечисления указанное значение. Например:
<h:graphicImage name="error.png" library="images"
rendered="#{viewController.current.status == Status.ERROR}" />
Немного утомительно, чтобы определить метод в управляемом beand, который проверяет это для каждого значения перечисления, например.
public boolean isStateIsError() {
return current.getStatus() == Status.ERROR;
}
Есть ли более короткий/лучший способ сделать это?
Ответы
Ответ 1
До EL 3.0 невозможно импортировать перечисления в область EL. Однако вы можете просто рассматривать и сравнивать их как строки, т.е. Постоянное значение перечисления должно быть указано как показано ниже.
<h:graphicImage name="error.png" library="images"
rendered="#{viewController.current.status eq 'ERROR'}" />
Ответ 2
Я знаю, что этот вопрос немного устарел, но у меня была такая же проблема, и я нашел другое решение, которое хочу поделиться:
Создайте пользовательский EL-Resolver и используйте enum и java константы в качестве объектов в jsf el:
<h:graphicImage name="error.png" library="images"
rendered="#{viewController.current.status == Status.ERROR}" />
Но прежде чем вы сможете использовать перечисления таким образом, вам нужно сделать 3 шага.
1. step - Скопируйте этот класс и замените "MY_ENUM" через ваш enumClass (в приведенном выше примере это будет "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. step - добавьте этот 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 - зарегистрируйте EnumResolver в faces-config.xml
<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) { }
}
Надеюсь, что этот сокращенный, но рабочий код может помочь кому угодно.
Обновление
Я вижу это преимущество:
-
Если вы используете строки в jsf (viewController.current.status == 'ERROR_abcdefg), вы можете опечатать это значение и не будете распознавать его так быстро.
С моим решением вы получили бы ошибку при загрузке jsf файла, потому что переименование не могло быть разрешено.
-
В исходном коде вы можете увидеть, что "ОШИБКА" - значение перечисления "STATUS".
-
Когда вы сравниваете два значения в el, будет также сравниваться класс перечислений.
Так, например, PersonState.ACTIV отличается от AccounState.ACTIV.
-
Когда мне нужно изменить значение enum из PersonState.ACTIV в PersonState.ACTIVATED, я могу найти строку "PersonState.ACTIV" в моем исходном коде. поиск "ACTIV" будет иметь гораздо больше совпадений.
Ответ 3
Я решил аналогичную проблему statically
, сбросив все ключи перечисления (которые используются в визуализированных компонентах пользовательского интерфейса) на карте, а затем я использую статический метод getByKey
для преобразования значения из пользовательского интерфейса в фактический native enum в 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 ключ перечисления используется как значение, а метка enum используется как метка:
<f:selectItems var="rptTypeItem" value="#{reportController.allReportTypes}"
itemLabel="#{rptTypeItem.label}" itemValue="#{rptTypeItem.key}"/>
В managed bean
я преобразую перечисление в отображаемый список, используя getValueList()
из перечисления:
public List<ReportType> getAllReportTypes() {
return ReportType.getValueList();
}
Наконец, [g | s] etters в управляемом bean выглядят следующим образом:
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]}" />
предполагая, что порядок членов перечисления не будет изменен (например, здесь статусы [0] - ОШИБКА). Однако я бы зафиксировал такие позиции:
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. Может быть особенно полезно, когда вы используете локализацию для своих статусов (значения перечисления в зависимости от языка/перевода).
Ответ 5
Я решаю свою проблему, используя:
<p:graphicImage name="images/close.png" rendered="#{i.unityEnum.name().equals('DAY')}" />