JSF PrimeFaces Dialogの入力結果をFormに反映する

#はじめに
JSFプログラミングにてFormからDialogをポップアップさせて、Dialog上のコンボボックス(selectOneMenu)の選択結果を元のフォームに反映させる方法の紹介です。
JavaEE上の開発になりますが、アプリケーションサーバにGlassFish、Dialogのライブラリに[PrimeFaces](http://www.primefaces.org/)を使用しています。IDEはnetbeansを使っています。
NetBeansインストール方法は[こちら](http://qiita.com/yu_naka0607/items/c0dca687a6659f7e9ac4)を参照ください。
Projectの作成、PrimeFaces設定方法は[こちら](http://qiita.com/BlueMoonAZ/items/b48abba5b5d665a33c05)を参照下さい。
JavaEEの入門には[こちら](http://qiita.com/tkxlab/items/3c0d4073defacb1215f5)がおススメです。

選択ボタンをクリックするとチーム一覧が表示され、任意チームのselectボタンをクリックすると一覧画面が閉じてフォームに入力される画面を作ってみました。

![image](https://qiita-image-store.s3.amazonaws.com/0/94957/63a528e6-26fc-e32d-6c14-6938fe0debb9.png)


#環境
NetBeans 8.0.2
JavaSDK 1.8.0.25
PrimeFaces 5.0
GlassFish 4.1

#configファイル追加
PrimeFacesのマニュアルに記載されていますが、DialogFrameworkを有効とするためfaces-config.xmlファイルをWEB-INFフォルダに作成します。そして以下のエントリを追加します。

```xml:faces-config.xml
    <application>
        <action-listener>org.primefaces.application.DialogActionListener</action-listener>
        <navigation-handler>org.primefaces.application.DialogNavigationHandler</navigation-handler>
        <view-handler>org.primefaces.application.DialogViewHandler</view-handler>
    </application>
```


#Dialogの表示
PrimeFacesのDialogFrameworkを利用すれば、作成したJSF画面をDialogとして表示することができます。BackingBeanではDialogとして表示したいJSFページをopenDialogメソッドの引数に指定します。

```java
public void viewTeams() {
 RequestContext.getCurrentInstance().openDialog("viewTeams");
}
```

Dialogの親フォームではcommandButtonのリスナーにメソッドを指定し、ButtonClickの契機でDialogを表示させます。

```html
<p:commandButton value="選択" actionListener="#{bb.viewTeams()}" />
```

#Dialogの入力結果取得
dataTableの選択イベントにて、DialogFrameworkのcloseDialogメソッドを呼ぶことでDiaolgのクローズと親フォームへの選択結果渡しが同時にできます。

```java
public void selectTeamFromDialog(String teams) {
        RequestContext.getCurrentInstance().closeDialog(teams);
}
```

親フォームではDialog選択結果を受け取るため、ajaxタグを使用してdialogReturnイベントをフックします。選択結果はBackingBeanのメンバに格納するためlistenerを指定します。また、メンバの内容を表示するinputTextのidをupdateに指定します

```html
        <p:commandButton value="選択" actionListener="#{bb.viewTeams()}" >
            <p:ajax event="dialogReturn" listener="#{bb.handleReturn}" 
                     update="inputTeam" />
        </p:commandButton>
```

BackingBeanではSelectEvent型の引数から選択結果を取得します。

```java
 public void handleReturn(SelectEvent event) {
        team = (String) event.getObject();
        
 }
```

#プログラムコード
##JSF

| ファイル名 | 説明 |
|:-----------|:------------|
|index.xhtml|親Formのページ|
|viewTeams.xhtml|Dialogとして表示されるページ|

```html:index.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    
    <h:head>
        <title>favorite team</title>
    </h:head>
    <h:body>

        <h:form id="form1">
        好きなチームは？
        <h:inputText id="inputTeam" value="#{bb.team}" />
        <p:commandButton value="選択" actionListener="#{bb.viewTeams()}" >
            <p:ajax event="dialogReturn" listener="#{bb.handleReturn}" update="inputTeam" />
        </p:commandButton>           
        </h:form>
        
    </h:body>
    
</html>
```

```html:viewTeams.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head>
        <title>Teams</title>
        <style type="text/css">
            .ui-widget {
                font-size: 90%;
            }
        </style>
    </h:head>
    <h:body>
        <h:form>
            <p:selectOneMenu value="#{teamList.league}">
                <f:selectItem itemValue="セリーグ" itemLabel="セリーグ" />
                <f:selectItem itemValue="パリーグ" itemLabel="パリーグ" />
                <p:ajax event="change" update="teamTable" />             
            </p:selectOneMenu>    
            
            <p:dataTable id="teamTable" var="team" value="#{teamList.teams}" style="width:200px">
                <p:column headerText="チーム" style="width:50px;text-align: center">
                    <h:outputText value="#{team}" />
                </p:column>
                <p:column headerText="select" style="width:32px;text-align: center">
                    <p:commandButton icon="ui-icon-search"
                                 actionListener="#{bb.selectTeamFromDialog(team)}" />
                </p:column>            
            </p:dataTable>
        </h:form>
    </h:body>
</html>
```

##Java

| ファイル名 | 説明 |
|:-----------|:------------|
|Bb.java|親FormとDialogのBackingBean|
|TeamList.java|DialogのdataList用メンバを生成して保持する|

```java:Bb.java
package beans;

import java.util.*;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
import org.primefaces.context.RequestContext;
import org.primefaces.event.SelectEvent;
 
@Named()
@RequestScoped
public class Bb {
    
    String team;
    
    public void viewTeams() {
        Map<String,Object> options = new HashMap<>();
        options.put("width", 250);
        RequestContext.getCurrentInstance().openDialog("viewTeams",options,null);
    }

    public void selectTeamFromDialog(String teams) {
        RequestContext.getCurrentInstance().closeDialog(teams);
    }
    
    public void handleReturn(SelectEvent event) {
        team = (String) event.getObject();
        
    }
    
    public void selectTeam(String team) {
        this.team=team;
    }

    public String getTeam() {
        return team;
    }

    public void setTeam(String team) {
        this.team = team;
    }
    
}
```

```java:TeamList.java
package beans;

import java.util.*;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@Named
@RequestScoped
public class TeamList {

    private List<String> teams=new ArrayList();
    private String league;
    
    @PostConstruct
    public void init(){
        league="セリーグ";
        changeLeague();     
    }

    public List<String> getTeams() {
        return teams;
    }

    public void setTeams(List<String> teams) {
        this.teams = teams;
    }

    public String getLeague() {
        return league;
    }

    public void setLeague(String league) {
        this.league = league;
        changeLeague();
    }
  
    
    public void changeLeague() {
        
        if(league == null)return;
        
        if(league.equals("セリーグ") ){
            teams=new ArrayList();
            teams.add("スワローズ");
            teams.add("ジャイアンツ");
            teams.add("タイガース");
            teams.add("カープ");
            teams.add("ドラゴンズ");
            teams.add("ディーエヌエー");          
        } else{
            teams=new ArrayList();
            teams.add("ホークス");
            teams.add("ファイターズ");
            teams.add("マリーンズ");
            teams.add("オリックス");
            teams.add("ライオンズ");
            teams.add("ゴールデンイーグルス");
        }
        
    }    
}
```
Poolから使用済みのbeanが割り当てられる可能性があるので、PostConstructアノテーションを指定したinitメソッドで初期化を行っています。

#終わりに
簡単な画面制御と値の受け渡しでしたが、javascriptを使うことなくコード作成出来ました。フレームワークではタグライブラリなど使用するとjavascriptを自動生成するケースが有るので、直接javascriptを書く場合はフレームワークとの連携が難しい場合が有りました。javascriptで書いていた部分をPrimeFacesがフォローする分、スッキリとしたコードになりました。