Подтвердить что ты не робот

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;
}

Есть ли более короткий/лучший способ сделать это?

4b9b3361

Ответ 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')}" />