概要
セレクトボックスの値が非同期で変更するものを作ってみました。
例
- セレクトボックスで群馬県を選択
- 次のセレクトボックスには群馬県内の市だけが選択肢に入る
環境
Java13/JPA/JavaScript
参考
Ajaxを使った非同期通信
@ResponseBodyについて
ResponseEntityクラスについて
ソースコード
hello.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Ajax!</title>
</head>
<body>
<form action="">
<div class="form-group">
<label th:for="ken">県</label>
<select id="ken" class="form-control">
<option>選択してください</option>
<option id="kenOption" th:each="ken : ${kenList}" th:text="${ken.kenName}" th:value="${ken.kenCode}"></option>
</select>
</div>
<div class="form-group">
<label th:for="city">市</label>
<select id="city" class="form-control">
<option>選択してください</option>
</select>
</div>
</form>
<script src="ajax.js"></script>
</body>
</html>
ajax.js
'use strict';
//要素の取得
const ken = document.getElementById('ken');
const city = document.getElementById('city');
// slectboxのchangeイベントで発火する関数
function ajax() {
//選択された県コードの取得
let kenCode = ken.value;
let request = new XMLHttpRequest();
// onreadystatechangeは非同期。状態が変化するとコールバック関数が呼び出される。
request.onreadystatechange = function() {
//readyState4はレスポンスの受信完了
if(request.readyState === 4) {
// status200はサーバーとの通信OK
if(request.status === 200) {
// レスポンスの取得
let resultJson = request.response;
console.log(resultJson);
let cityData;
// optionタグをresultJson配列の数だけ作る
for(let i = 0; i < resultJson.length; i++) {
cityData += `<option th:value="${resultJson[i].cityCode}">${resultJson[i].cityName}</option>`
}
city.innerHTML = cityData;
}else {
city.innerHTML = '<option>通信失敗(´;ω;`)</option>';
}
}
}
request.open('GET', '/hello/ajax?kenCode=' + kenCode, true);
request.responseType = 'json';
request.send(null);
}
ken.addEventListener('change', ajax);
request.send(null)
でリクエスト送信前に、let resultJson = request.response
でレスポンス受け取ってるのは変な気がするけどこれでOK。
基本は同期処理完了→非同期処理完了の順番なので、リクエスト送信後にレスポンスを受け取る流れになる。
AjaxController.java
@Controller
public class AjaxController {
@Autowired
AjaxService ajaxService;
// 画面表示
@GetMapping("/hello")
public String getHello(Model model) {
List<AjaxEntity> kenList = ajaxService.getKenNameList();
model.addAttribute("kenList", kenList);
return "hello";
}
// 非同期通信。県コードを受け取ってcityリストを返す。
@GetMapping("/hello/ajax")
@ResponseBody
public ResponseEntity<List<AjaxEntity>> postAjax(@RequestParam("kenCode") int kenCode) {
List<AjaxEntity> list = ajaxService.getCityNameList(kenCode);
try {
//通信成功
return new ResponseEntity<List<AjaxEntity>>(list, HttpStatus.OK);
} catch(Exception e) {
//通信失敗
return new ResponseEntity<List<AjaxEntity>>(HttpStatus.BAD_REQUEST);
}
}
}
@ResponseBodyをつけると@Controllerの戻り値にString以外も指定できる。
今回はEntityクラスのList(配列)をResponseEntity入れて戻り値にしている。
@RestControllerでも似たようなことができるっぽい。
AjaxService.java
@Service
public class AjaxService {
@Autowired
AjaxRepository ajaxRepository;
/**
* 全県名と全県コードのリストを取得する
*/
public List<AjaxEntity> getKenNameList(){
List<String[]> list = ajaxRepository.getKenNameList();
List<AjaxEntity> aeList = new ArrayList<>();
for(String[] array : list) {
int kenCode = Integer.parseInt(array[0]);
String kenName = String.valueOf(array[1]);
AjaxEntity ae = new AjaxEntity();
ae.setKenCode(kenCode);
ae.setKenName(kenName);
aeList.add(ae);
}
return aeList;
}
/**
* 同一県内の市をリストで返す
*
* 例:群馬県を選択すると、群馬県内の市リストが返る
*/
public List<AjaxEntity> getCityNameList(int kenCode){
List<String[]> list = ajaxRepository.getCityNameList(kenCode);
List<AjaxEntity> aeList = new ArrayList<>();
for(String[] array : list) {
int cityCode = Integer.parseInt(array[0]);
String cityName = String.valueOf(array[1]);
AjaxEntity ae = new AjaxEntity();
ae.setCityCode(cityCode);
ae.setCityName(cityName);
aeList.add(ae);
}
return aeList;
}
}
AjaxRepository.java
@Repository
public interface AjaxRepository extends JpaRepository<AjaxEntity, Integer>{
@Query(value = "select distinct kenCode, kenName from AjaxEntity a")
List<String[]> getKenNameList();
@Query(value = "select distinct cityCode, cityName from AjaxEntity a where kenCode = :kenCode")
List<String[]> getCityNameList(@Param("kenCode") int kenCode);
}
AjaxEntity.java
@Data
@Entity
@Table(name="address")
public class AjaxEntity {
@Id
@Column(name="kencode")
private Integer kenCode;
@Column(name="citycode")
private Integer cityCode;
@Column(name="kenname")
private String kenName;
@Column(name="cityname")
private String cityName;
}