web-dev-qa-db-ja.com

JSF 2:レンダリングされた属性で列挙型を使用する

列挙型に指定された値があるかどうかを宣言的にチェックする方法はありますか?例えば:

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

マネージドBeanで、すべての列挙値についてこれをチェックするメソッドを定義するのは少し面倒です。

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

これを行うためのより短い/より良い方法はありますか?

19
Theo

EL 3.0まで ELスコープに列挙型をインポートすることはできません。ただし、文字列のように扱って比較することはできます。つまり、列挙型定数値は以下のように引用符で囲む必要があります。

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

私はこの質問が少し古いことを知っていますが、私は同じ問題を抱えていて、私が共有したい別の解決策を見つけました:

カスタムEL-Resolverを作成し、列挙型とJava定数をjsfのオブジェクトとして使用 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) {
    }
}

。ステップ-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) { }
}

これが減ったが、機能するコードが誰にでも役立つことを願っています。


更新

私はこの利点を見ます:

  1. Jsf(viewController.current.status == 'ERROR_abcdefg')で文字列を使用すると、値のスペルを間違えて、それほど速く認識されない可能性があります。私のソリューションでは、列挙型を解決できなかったため、jsfファイルのロード中にエラーが発生しました。

  2. ソースコードで、「ERROR」が列挙型「STATUS」の値であることがわかります。

  3. Elの2つの値を比較すると、列挙型のクラスも比較されます。したがって、たとえば、PersonState.ACTIVはAccounState.ACTIVと同じではありません。

  4. 列挙値をPersonState.ACTIVからPersonState.ACTIVATEDに変更する必要がある場合、ソースコードで文字列「PersonState.ACTIV」を検索できます。 「ACTIV」を検索すると、一致するものがはるかに多くなります。

7
Witali Janzen

マップにすべての列挙型キー(レンダリングされたUIコンポーネントで使用される)をstaticallyダンプすることで同様の問題を解決し、静的なgetByKeyメソッドを使用してUIから値を変換しますセッターの実際のネイティブ列挙型に、指定された値が無効な場合に例外をスローします。

_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]セッターは次のようになります。

_public String getReportType() {
    return this.crtRptType.getKey();
}

public void setReportType(String _val) {
    this.crtRptType = ReportType.getByKey(_val);
}
_
1
amphibient

私はそれが次の方法でそれを行うことができると思います:

たとえば、列挙型のリストを返すメソッドをBeanに作成します。

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

次に、このようにELで列挙型を使用できます

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

列挙型メンバーの順序が変更されないと仮定します(たとえば、ここではstatuss [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でハードコーディングするよりはましです。ステータス(ロケール/翻訳に応じた列挙値)にローカリゼーションを使用している場合に特に役立つことがあります。

1
sergeyan