Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

SpringBoot + Thymeleaf + Ajaxで 2つのドロップダウンリストを連動させる方法

概要

  • SpringBoot + thymeleaf + mysqlでWebアプリケーション開発時、二つの 2つのselectBoxを連動させる方法を紹介します。

機能

  • ひとつ目のドロップダウンリストの都道府県により、2つのドロップダウンリストの値は該当県の市区町に変わる。

開発環境

  • Windows 10
  • Spring Tool Suite 4 Version: 4.8.1.RELEASE
  • JavaSE-11
  • MySql 5.8
  • Thymeleaf 3.0.12

事前準備

Mysqlに都道府県と市区町のテーブルを作ってデータを登録する

都道府県テーブルを作成する

create_pref
CREATE TABLE `pref` (
  `prefCode` int(11) NOT NULL,
  `prefName` varchar(50) NOT NULL,
  `disFlag` int(11) DEFAULT '1',
  PRIMARY KEY (`prefCode`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

市区町テーブルを作成する

create_city
CREATE TABLE `city` (
  `prefCode` int(11) NOT NULL,
  `cityCode` int(11) NOT NULL,
  `cityName` varchar(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8

データを登録する

今回は首都圏付近の都道府県データの使用する
insert into pref(prefCode,prefName,disFlag)values(8,'茨城県',0);
insert into pref(prefCode,prefName,disFlag)values(9,'栃木県',0);
insert into pref(prefCode,prefName,disFlag)values(10,'群馬県',0);
insert into pref(prefCode,prefName,disFlag)values(11,'埼玉県',0);
insert into pref(prefCode,prefName,disFlag)values(12,'千葉県',0);
insert into pref(prefCode,prefName,disFlag)values(13,'東京都',0);
insert into pref(prefCode,prefName,disFlag)values(14,'神奈川県',0);
市区町データ(部分)
insert into city(`prefCode`,`cityCode`,`cityName`) values (8,201,'水戸市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (8,202,'日立市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (8,203,'土浦市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (8,204,'古河市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (8,205,'石岡市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (8,207,'結城市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (8,208,'龍ヶ崎市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (8,210,'下妻市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (8,211,'常総市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (9,201,'宇都宮市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (9,202,'足利市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (9,203,'栃木市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (9,204,'佐野市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (9,205,'鹿沼市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (9,206,'日光市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (9,208,'小山市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (9,209,'真岡市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (9,210,'大田原市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (9,211,'矢板市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (10,201,'前橋市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (10,202,'高崎市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (10,203,'桐生市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (10,201,'前橋市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (10,202,'高崎市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (10,203,'桐生市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (10,201,'前橋市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (10,202,'高崎市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (10,203,'桐生市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (10,204,'伊勢崎市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (10,205,'太田市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (10,206,'沼田市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (11,100,'さいたま市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (11,101,'西区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (11,102,'北区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (11,103,'大宮区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (11,104,'見沼区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (11,105,'中央区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (11,106,'桜区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (11,107,'浦和区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (11,108,'南区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (11,109,'緑区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (11,110,'岩槻区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (11,201,'川越市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (11,202,'熊谷市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (11,203,'川口市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,100,'千葉市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,101,'中央区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,100,'千葉市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,101,'中央区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,102,'花見川区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,103,'稲毛区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,104,'若葉区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,105,'緑区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,106,'美浜区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,202,'銚子市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,203,'市川市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,204,'船橋市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,205,'館山市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,206,'木更津市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,207,'松戸市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,208,'野田市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,424,'白子町');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,426,'長柄町');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,427,'長南町');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,440,'夷隅郡');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,441,'大多喜町');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,443,'御宿町');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,460,'安房郡');
insert into city(`prefCode`,`cityCode`,`cityName`) values (12,463,'鋸南町');
insert into city(`prefCode`,`cityCode`,`cityName`) values (13,100,'特別区部');
insert into city(`prefCode`,`cityCode`,`cityName`) values (13,101,'千代田区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (13,102,'中央区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (13,103,'港区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (13,104,'新宿区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (13,105,'文京区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (13,106,'台東区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (13,107,'墨田区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (13,108,'江東区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (13,109,'品川区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (13,110,'目黒区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,100,'横浜市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,101,'鶴見区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,102,'神奈川区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,103,'西区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,104,'中区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,105,'南区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,106,'保土ヶ谷');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,107,'磯子区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,108,'金沢区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,109,'港北区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,110,'戸塚区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,111,'港南区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,112,'旭区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,113,'緑区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,114,'瀬谷区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,115,'栄区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,116,'泉区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,117,'青葉区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,118,'都筑区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,130,'川崎市');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,131,'川崎区');
insert into city(`prefCode`,`cityCode`,`cityName`) values (14,132,'幸区');

アプリの作成

Entity作成

Pref.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Pref {
    private int prefCode;
    private String prefName;
}

City.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class City {
    private int prefCode;
    private int cityCode;
    private String cityName;
}

HTML画面作成

index.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<meta charset="utf-8">
<script>
//ここにAjaxコードを追加
</script>
<!-- main start -->
<div class="main">
    <div class="container-fluid">
        <!-- from start-->
        <form id="userform" name="userform" th:fragment="userform" th:method="post" th:action="@{/user}" th:object="${user}" class="needs-validation" novalidate>
            <!-- contents start-->
            <div class="contents" id="contents">
                <div class="contents-header">ユーザ登録</div>
                <!-- contents-body start-->
                <!-- 出身 -->
                <div class="form-group row  col-sm-12">
                    <label for="nutag" class="col-sm-2 col-form-label"> 出 身<span class="badge badge-secondary ml-2">任意</span>
                    </label>
                    <div class="col-sm-3">
                        <select class="form-control" th:field="*{nutagPref}">
                            <option th:each="pref:${prefData}" th:value="${pref.key}" th:text="${pref.value}" />
                        </select>
                    </div>
                    <div class="col-sm-2">
                        <select class="form-control" th:field="*{nutagCity}">
                            <option th:each="city:${cityList}" th:value="${city.cityCode}" th:text="${city.cityName}" />
                        </select>
                    </div>
                </div>
                <!-- contents-body end-->
                <div class="contents-footer border-0" id="contents-footer-insert">
                    <button type="submit" id="insertbtn" class="btn btn-primary btn-lg btn-block">上記内容で登録する</button>
                </div>
            </div>
            <!-- contents end-->
        </form>
        <!-- from-end-->
    </div>
    <!-- container-fluid -->
</div>
<!-- main-end-->
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>

</html>

  • 説明
    • userオブジェクトから値を取得してth:field="*{nutagPref}"th:field="*{nutagCity}"として selectboxに設定している
    • th:field="*{nutagPref}" - 都道府県ドロップダウンリスト
    • th:field="*{nutagCity}" - 市区町ドロップダウンリスト
<select class="form-control" th:field="*{nutagPref}">
    <option th:each="pref:${prefData}" th:value="${pref.key}" th:text="${pref.value}" />
</select>
<select class="form-control" th:field="*{nutagCity}">
    <option th:each="city:${cityList}" th:value="${city.cityCode}" th:text="${city.cityName}" />
</select>

コントロールクラスの作成

userController.java
@Controller
@RequestMapping
public class UserController {
    /**
     * ユーザー登録画面初期表示
     * 
     * @param model Model
     * @return ユーザー登録画面
     */
    @GetMapping("/user/new")
    public String newUser(Model model) {
        User user = setUserForm();
        // 初期表示の時selectboxに埋め込む都道府県データ
        model.addAttribute("prefData", divisionManager.getPrefData());
        // 初期表示の時selectboxに埋め込む市区町リスト
        model.addAttribute("cityList", divisionManager.getCity(NUTAGCITY_TOKYO));
        model.addAttribute("user", user);
        return "index";
    }
}

ここまで設定した値は画面に表示することを確認できる
image.png

ここからはポイントです
index.htmlの//ここにAjaxコードを追加を以下のコードで切り替える。

        // 出身情報処理
        $('#nutagPref').change(function() {
                    //入力値をセット----①
                    var param = {
                        prefCode : $("#nutagPref").val(),
                        prefName : $("#nutagPref option:selected").text(),
                    }
                    //県コードを送信URL
                    var send_url = "/nutag"; //----②
                    $.ajax({
                        url : send_url,
                        type : "POST",
                        contentType : "application/json",
                        cache : false,
                        data : JSON.stringify(param),
                        dataType : "json",
                        success : function(res) {   //----③
                            var cityList = [];
                            for (var i = 0; i < res.length; i++) {
                                var city = "<option value="+res[i].cityCode+">"
                                        + res[i].cityName + "</option>";
                                cityList.push(city);
                            }
                            $("#nutagCity").html(cityList);
                        }
                    });
                });

  • 説明
    • ① 画面で選択した都道府県データ、prefCodeにより、市区町を検索していますがprefCodeだけを送信すると Pref.javaとマッチングできないためprefNameも同時にサーバ側へ渡すしている
    • localhost:8080/nutagへアクセスする時コントロールのメソッドを呼び出す。
    • ③サーバ側から値を取得成功なら市区町のドロップダウンリストに設定している。

ポイント: 市区町のドロップダウンリストを静的に生成しているのでサーバ側とデータ通信しているが画面全体の更新を行わず、selectboxの中身だけを更新する

次にコントロールクラスでlocalhost:8080/nutagにマッチングするメソッドを作成する

userController.java
@Controller
@RequestMapping
public class UserController {
    /**
     * ユーザー登録画面初期表示
     * 
     * @param model Model
     * @return ユーザー登録画面
     */
    @GetMapping("/user/new")
    public String newUser(Model model) {
        User user = setUserForm();
        // 初期表示の時selectboxに埋め込む都道府県データ
        model.addAttribute("prefData", divisionManager.getPrefData());
        // 初期表示の時selectboxに埋め込む市区町リスト
        model.addAttribute("cityList", divisionManager.getCity(NUTAGCITY_TOKYO));
        model.addAttribute("user", user);
        return "index";
    }

    /**
     * 出身連動選択
     * 
     * @param pref
     * @return 出身市区のList
     */
    @PostMapping("/nutag")
    @ResponseBody //------②
    public List<City> setCitySelectBox(@RequestBody Pref pref) { //------①
        List<City> cityList = divisionManager.getCity(pref.getPrefCode()); //------③
        return cityList;
    }
}
  • 説明
    • ① @RequestBodyを付けるとAjaxから送信したparamのデータが prefオブジェクトに入る
    • ② @ResponseBodyを付けるとメソッドの戻り値は Ajaxの'success : function(res)`に設定される
    • ③ 市区町データを'PrefCode'を条件に検索する

でき上げるもの

1.gif

終了

jsj05543
ITは武器
Leading-Edge
ITエンジニアの生涯価値向上を目指し、派遣・紹介・教育・自社開発など様々な分野から全方位支援を行っております。
https://www.leadinge.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away