読者です 読者をやめる 読者になる 読者になる

Challenge Java EE !

Java EEを中心に趣味や仕事における開発メモを書いています。

FacesContextのevaluateExpressionGetなるメソッド

JSF

開発メモ。特殊なケースなので、あまり参考にせず、最終手段としての方法かなと。

JSFのFacesContextでは様々な情報を管理していますが、Applicationクラスが持つevaluateExpressionGet()なる黒魔術なメソッドを知りました。

https://javaserverfaces.java.net/docs/2.2/javadocs/javax/faces/application/Application.html

簡単にいうと、コード側からEL式を記述すると、そのEL式を評価して値を取ってくれます。

例えば、以下のようなコードです。

FacesContext context = FacesContext.getCurrentInstance();
boolean beanChecked =
    context.getApplication().evaluateExpressionGet(context, "#{actressBean.checked}", boolean.class);

画面に置いたチェックボックスをActressBeanという管理Beanのcheckedというboolean変数にバインドしているイメージです。

上記コードでは、その値をいつでも取れるという……なにこれ。

注意

これをあちこちで多用し出すと(ユニット)テストが死ぬので、なるべく使わないほうがいいと思います。

JSFはそもそもFacesContextを利用し出すと、モックで頑張るか、Arquillianなどを使うか、となっていくので、この辺を意識して上手く作らないとテストしやすいコードにならなかったり。(だから.NETもWebFormからMVCにいったのだし…)

以下は補足と備忘で、どんな状況でこれを使いたくなかったのか?書いておきます。

やろうとしたこと

元々やりたかったのは、PrimeFacesのajaxにおいて、EL式からバインドしてる呼出し先へパラメータを渡したいというものでした。

PrimeFacesではTreeコンポーネントのノードを選択したイベントを以下のようなajaxで書きます。

<p:tree var="act" value="#{actressBean.tree}" 
    selection="#{actressBean.selectedNode}" selectionMode="single">

    <!-- ノードの選択イベント -->
    <p:ajax event="select" listener="#{act.hello}" />
    
    <p:treeNode>
        <h:outputText value="#{act.name}" />
    </p:treeNode>
</p:tree>

ちなみにイベントにはselectの他にもツリーノードを展開したか閉じたか(expand/collapse)などもあります。
http://www.primefaces.org/showcase/ui/data/tree/events.xhtml

で、EL式で書いているactというのは、以下Actressクラスで、管理Beanではない普通のPOJOです。
helloは引数にPrimeFacesのNodeSelectEventを指定して、ここに画面で選択されたノード情報が入ってきます。

@AllArgsConstructor
@Getter @Setter
public class Actress {
    private String name;
    private int age;
    
    public Actress(String name){
        this.name = name;
    }
    
    public void hello(NodeSelectEvent event){
        Actress me = ((Actress)event.getTreeNode().getData());
        System.out.println("画面でクリックされたのは"+ me.name);
    }
}

で、このときに、EL式から呼んでるhelloに引数を渡したいケースがあったのですが、呼び出すメソッドとEL式に引数を追加しても、シグネチャが合わなくてメソッドがみつからない、と言われました。

<!-- 引数つけて呼び出したいけどhelloに引数追加しても呼べない -->
<p:ajax event="select" listener="#{act.hello(actressBean.checked)}" />

元ネタ

で、ググっていったら同じ質問が以下にありました。


java - JSF (and PrimeFaces) How to pass parameter to a method in ManagedBean - Stack Overflow

PrimeFaces Community Forum • View topic - Ajax Events with arguments

前者のStackOverflowの回答にevaluateExpressionGetを利用した回答があり、やってみたら動いたという。

ということで、こんな感じに。

public void hello(NodeSelectEvent event){
    Actress me = ((Actress)event.getTreeNode().getData());
    System.out.println("画面でクリックされたのは"+ me.name);
        
    //強引にActressBeanのchecked値をとってくる
    FacesContext context = FacesContext.getCurrentInstance();
    boolean beanChecked =
        context.getApplication().evaluateExpressionGet(context, "#{actressBean.checked}", boolean.class);
        
    System.out.println(beanChecked);
}

実際に今書いているコードに比べて上記の例だと、なんでそんな作りしてるんじゃ、って感じになりそうですが…良い例を書く時間が取れなかったのでここまで。

おまけ

@megascusさんはこのメソッドをご存じだったようで

とのこと…すごいなぁ。

3年くらいJSF使ってて初めて知りました(^^;

にほんブログ村 IT技術ブログへ
にほんブログ村
にほんブログ村 IT技術ブログ Javaへ
にほんブログ村