【JSF】ajaxタグを用いたセレクトボックス要素の動的取得

#概要
JSFの非同期通信タグ`<f:ajax>`を用いて
１つ目のセレクトボックスと２つ目のセレクトボックス内容を連動させてみる。

#環境

 - IDE : Eclipse 4.7 Oxygen
 - APサーバ : GlassFish 4.1.1
 - JDK 1.8
 - OS : Windows7
 - [プロジェクトの設定](http://qiita.com/nifram/items/123ec5689da654b365aa#eclipse%E3%81%AE%E8%A8%AD%E5%AE%9A)

#完成イメージ

【図１．完成図】
![ajax4.png](https://qiita-image-store.s3.amazonaws.com/0/190666/2a743e96-4216-cc17-f2a6-9107b19d6b23.png)

初期状態では２つ目のセレクトボックスは空です。

１つめのセレクトボックスで人物を選択すると

２つめのセレクトボックスにその人物が持っているスキルのリストが組み込まれ、選択できる状態になります。

#実装（１）

まずはDTOを利用せず、リストの要素に
**SelectItem**クラスを利用したパターンから。（javax.faces.model.SelectItem）
※DTO：[IT用語辞典](http://e-words.jp/w/DTO.html)

```SelectBean.java
package bean;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.model.SelectItem;

@ManagedBean
@ViewScoped
public class SelectBean implements Serializable{

	/*** 本来DB等から拾って来るデータ ***************************************************/
	//従業員マスタ
	static List<SelectItem> EMPLOYEE_LIST;
	static {
		EMPLOYEE_LIST = new ArrayList<SelectItem>();
		EMPLOYEE_LIST.add(new SelectItem("E0000","選択して下さい"));
		EMPLOYEE_LIST.add(new SelectItem("E0001","テスト一郎"));
		EMPLOYEE_LIST.add(new SelectItem("E0002","テスト二郎"));
		EMPLOYEE_LIST.add(new SelectItem("E0003","テスト三郎"));
	}

	//スキルマスタ
	private static Map<Object,List<SelectItem>> SKILL_MAP;
	static {
		List<SelectItem> emp1Skills = new ArrayList<SelectItem>();
		emp1Skills.add(new SelectItem("skill_01","Java"));
		emp1Skills.add(new SelectItem("skill_02","JSP"));
		emp1Skills.add(new SelectItem("skill_03","JavaScript"));

		List<SelectItem> emp2Skills = new ArrayList<SelectItem>();
		emp2Skills.add(new SelectItem("skill_01","Java"));
		emp2Skills.add(new SelectItem("skill_03","JavaScript"));
		emp2Skills.add(new SelectItem("skill_05","C++"));

		List<SelectItem> emp3Skills = new ArrayList<SelectItem>();
		emp3Skills.add(new SelectItem("skill_04","C"));
		emp3Skills.add(new SelectItem("skill_05","C++"));
		emp3Skills.add(new SelectItem("skill_06","VB.net"));

		SKILL_MAP = new HashMap<Object,List<SelectItem>>();
		SKILL_MAP.put("E0001", emp1Skills);
		SKILL_MAP.put("E0002", emp2Skills);
		SKILL_MAP.put("E0003", emp3Skills);
	}
	/********************************************************************************/

	//選択された従業員
	private String selectedEmployee;
	//従業員リスト
	private List<SelectItem> empList = EMPLOYEE_LIST;
	//スキルリスト
	private List<SelectItem> skillList;

	/*** method ****************************************************************/
	public void changeSkillList(){
		this.skillList = SKILL_MAP.get(this.selectedEmployee);
	}

	/*** getter setter *********************************************************/
	public String getSelectedEmployee() {
		return selectedEmployee;
	}

	public void setSelectedEmployee(String selectedEmployee) {
		this.selectedEmployee = selectedEmployee;
	}

	public List<SelectItem> getSkillList() {
		return skillList;
	}

	public void setSkillList(List<SelectItem> skillList) {
		this.skillList = skillList;
	}

	public List<SelectItem> getEmpList() {
		return empList;
	}

	public void setEmpList(List<SelectItem> empList) {
		this.empList = empList;
	}

}
```
```index.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://xmlns.jcp.org/jsf/html"
	xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
	<title>Ajax</title>
</h:head>
<h:body>

<h:form>
	<!-- ajaxによる非同期通信をあれこれしている部分 -->
	<div>
		<f:ajax event="change" render="skill" listener="#{selectBean.changeSkillList()}">
			<h:selectOneMenu value="#{selectBean.selectedEmployee}">
				<f:selectItems value="#{selectBean.empList}" />
			</h:selectOneMenu>
		</f:ajax>
	</div>

	<!-- ajaxによる非同期通信後 更新されるコンポーネント -->
	<div>
		<h:selectOneMenu id="skill">
			<f:selectItems value="#{selectBean.skillList}" />
		</h:selectOneMenu>
	</div>
</h:form>

</h:body>
</html>
```
まず今回DBを用いていないのでマスタデータは適当に用意しています。

フィールドとして
１．選択された従業員のID
２．従業員リスト
３．選択された従業員のスキルリスト

を持たせます。

`<f:ajax>`タグのlistenerに呼び出されるメソッドの中で
マスタのSKILL_MAPの中から「選択された従業員のID」に対応するスキルリストを取得して
フィールドの「選択された従業員のスキルリスト」に格納します。

今回`<f:ajax>`タグで用いられている属性を簡単に説明します。

 - event　：change(値が変更された)、keyup(キーボードのキーが上がった)等様々なタイミングでajaxの処理を開始します。
 - render　：ajaxの処理が終わり、最終的に更新したいコンポーネントを指定します。複数ある場合は半角スペース区切りで指定します。
 - listener　：eventが確認された際、呼び出すメソッドを指定します。

#実装（２）

次にDTOクラスを利用したいパターン。
※DTO：[IT用語辞典](http://e-words.jp/w/DTO.html)

まずDTO、従業員とスキルの２クラスを定義します。

```Employee.java
package dto;

/* 従業員クラス */
public class Employee {

	private String id ;
	private String name ;

	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

```
```Skill.java
package dto;

/* スキルクラス */
public class Skill {

	private String skillId;
	private String skillName;

	public String getSkillId() {
		return skillId;
	}
	public void setSkillId(String skillId) {
		this.skillId = skillId;
	}
	public String getSkillName() {
		return skillName;
	}
	public void setSkillName(String skillName) {
		this.skillName = skillName;
	}
}




```
次に管理Bean。

```SelectBean.java
package bean;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

import dto.Employee;
import dto.Skill;

@ManagedBean
@ViewScoped
public class SelectBean implements Serializable{

	/*** 本来DBから拾って来るデータ ***************************************************/
	//従業員マスタ
	static List<Employee> EMPLOYEE_LIST;
	static {
		EMPLOYEE_LIST = new ArrayList<Employee>();
		EMPLOYEE_LIST.add(new Employee("E0000","選択して下さい"));
		EMPLOYEE_LIST.add(new Employee("E0001","テスト一郎"));
		EMPLOYEE_LIST.add(new Employee("E0002","テスト二郎"));
		EMPLOYEE_LIST.add(new Employee("E0003","テスト三郎"));
	}

	//スキルマスタ
	private static Map<Object,List<Skill>> SKILL_MAP;
	static {
		List<Skill> emp1Skills = new ArrayList<Skill>();
		emp1Skills.add(new Skill("skill_01","Java"));
		emp1Skills.add(new Skill("skill_02","JSP"));
		emp1Skills.add(new Skill("skill_03","JavaScript"));

		List<Skill> emp2Skills = new ArrayList<Skill>();
		emp2Skills.add(new Skill("skill_01","Java"));
		emp2Skills.add(new Skill("skill_03","JavaScript"));
		emp2Skills.add(new Skill("skill_05","C++"));

		List<Skill> emp3Skills = new ArrayList<Skill>();
		emp3Skills.add(new Skill("skill_04","C"));
		emp3Skills.add(new Skill("skill_05","C++"));
		emp3Skills.add(new Skill("skill_06","VB.net"));

		SKILL_MAP = new HashMap<Object,List<Skill>>();
		SKILL_MAP.put("E0001", emp1Skills);
		SKILL_MAP.put("E0002", emp2Skills);
		SKILL_MAP.put("E0003", emp3Skills);
	}
	/********************************************************************************/

	//選択された従業員
	private String selectedEmployee ;
	//従業員リスト
	private List<Employee> empList = EMPLOYEE_LIST;
	//スキルリスト
	private List<Skill> skillList;

	/*** method ****************************************************************/
	public void changeSkillList(){
		this.setSkillList(SKILL_MAP.get(selectedEmployee));
	}

	/*** getter setter *********************************************************/
	public String getSelectedEmployee() {
		return selectedEmployee;
	}

	public void setSelectedEmployee(String selectedEmployee) {
		this.selectedEmployee = selectedEmployee;
	}

	public List<Skill> getSkillList() {
		return skillList;
	}

	public void setSkillList(List<Skill> skillList) {
		this.skillList = skillList;
	}

	public List<Employee> getEmpList() {
		return empList;
	}

	public void setEmpList(List<Employee> empList) {
		this.empList = empList;
	}
}
```
記述はほぼ変更無しですが、SelectItemクラスを利用していた部分がDTOのクラスに変更されています。

最後にView。

```index.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://xmlns.jcp.org/jsf/html"
	xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
	<title>Ajax</title>
</h:head>
<h:body>

<h:form>
	<!-- ajaxによる非同期通信の -->
	<div>
		<f:ajax event="change" render="skill" listener="#{selectBean.changeSkillList()}">
			<h:selectOneMenu value="#{selectBean.selectedEmployee}">
				<f:selectItems value="#{selectBean.empList}" var="emp" itemLabel="#{emp.name}" itemValue="#{emp.id}"/>
			</h:selectOneMenu>
		</f:ajax>
	</div>

	<!-- ajaxによる非同期通信後 更新されるコンポーネント -->
	<div>
		<h:selectOneMenu id="skill">
			<f:selectItems value="#{selectBean.skillList}" var="sk" itemLabel="#{sk.skillName}" itemValue="#{sk.skillId}" />
		</h:selectOneMenu>
	</div>
</h:form>

</h:body>
</html>
```
変更された部分は、`<f:selectItems>`タグの中に
属性：var/itemLabel/itemValue
を追加している点です。

#補足

##非同期通信とは
クライアントがページ全体をリクエストして、サーバからの応答を待っている間何も出来ない同期通信に対し
非同期通信ではページの一部機能に必要な情報のみリクエストし、レスポンスが返ってくるまでの間も平行して他の処理を行うことが出来ます。
例の１つとして、検索エンジンのサジェスト機能がそれにあたります。

![サジェスト.png](https://qiita-image-store.s3.amazonaws.com/0/190666/3cdd96e1-75e7-24c2-be0d-a667b0ad9a9b.png)

keyup等のイベントで、キーが入力された都度検索候補の情報をリクエストしているのだと思います。

※非同期通信：[＠IT  リッチクライアント用語辞典](http://www.atmarkit.co.jp/aig/07wcr/hidouki.html)