たなかのJava日記

どんなことをやったか(学んだか)、どこで詰まったか(わからなかったか)、どこで工夫したかの記録です。Dayで始まる記事は1日遅れで更新されることが多いです。

【Spring Boot】タスクの一覧表示機能のエンドポイントを実装

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>タスク管理アプリケーション</title>
</head>

    <body>
        <h1>タスク管理アプリケーション</h1>
    
        <div class="task_form">
            <h2>タスクの登録</h2>
    
            <form action="/add">
                <label>タスク</label>
                <input name="task" type="text" />
                <label>期限</label>
                <input name="deadline" type="date" />
                <input type="submit" value="登録" />
            </form>
        </div>
    
        <div class="tasklist">
            <h2>現在のタスクの一覧</h2>
            <table border="1" style="border-collapse:collapse;">
                <thead>
                    <tr><th class="hidden">ID</th><th>タスク</th><th>期限</th><th>状態</th></tr>
                </thead>
    
                <tbody>
                    <tr th:each="task :${taskList}">
                        <td class="hidden" th:text="${task.id}"></td>
                        <td th:text="${task.task}"></td>
                        <td width="100px" th:text="&{task.deadline}"></td>
                        <td width="50px" th:text="${task.done} ? '完了': '未完了'"></td>
                    </tr>
                </tbody>
            </table>
        </div>
    
    </body>
</html>


前回、上記のようなHTMLテンプレートが完成しました。
タスクの追加機能と一覧機能の部分をThymeleafを使って作成したのでした。
今回はHomeControllerクラス側(Java側)でこのHTMLテンプレートに対応するエンドポイントを作成します。


まず行うことは、タスクを表すTaskItemレコードと、それを格納するためのtaskItemフィールドを宣言することです。
理由はタスクを追加するのにも、現在の一覧を表示するのにも、
タスクの中身はなんなのか、格納する場所はどこなのかを決めないと始まらないからです。

package jp.gihyo.projava.tasklist;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.ArrayList;
import java.util.List;

@Controller
public class HomeController {
    record TaskItem(String id, String task, String deadline, boolean done) {}
    private List<HomeRestController.TaskItem> taskItems = new ArrayList<>();
}


この説明は以前まとめているので割愛します。

【record】違う種類の値をまとめて扱うレコードとは何が便利か・・・ - たなかのJava日記
【Spring Boot】タスクの情報を保持するためのモデルの作成 - たなかのJava日記


これで、格納場所などが決まりました。


次にタスクを一覧表示するエンドポイントを作成し追加します。
タスク一覧はHTMLで以下の部分になります。

        <div class="tasklist">
            <h2>現在のタスクの一覧</h2>
            <table border="1" style="border-collapse:collapse;">
                <thead>
                    <tr><th class="hidden">ID</th><th>タスク</th><th>期限</th><th>状態</th></tr>
                </thead>
    
                <tbody>
                    <tr th:each="task :${taskList}">
                        <td class="hidden" th:text="${task.id}"></td>
                        <td th:text="${task.task}"></td>
                        <td width="100px" th:text="&{task.deadline}"></td>
                        <td width="50px" th:text="${task.done} ? '完了': '未完了'"></td>
                    </tr>
                </tbody>
            </table>
        </div>


HTMLを確認すると${taskList}という変数が1タスクに該当していることにしていました。
なので、JavaのほうでtaskItemsのフィールドをtaskListという変数に渡せば良さそうです。

package jp.gihyo.projava.tasklist;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.ArrayList;
import java.util.List;

@Controller
public class HomeController {
    record TaskItem(String id, String task, String deadline, boolean done) {}
    private List<HomeRestController.TaskItem> taskItems = new ArrayList<>();

    @GetMapping("/list")
    String listItems(Model model) {
        model.addAttribute("taskList", taskItems);
        return "home";
    }
}


タスクを一覧表示するエンドポイントのメソッド名は、listItemsとしました。

ここでやりたいのは、タスク情報の読み取り(受け取り)です。
言い換えると、Webサーバからタスク情報を取り出す(GET)することです。
そのための(GETリクエスト用の)アノテーションである、@GetMappingをlistItemsメソッドの前に記載します。
アノテーションの()内にはvalue属性を記載します。
value属性はどのパス(URL)に対するリクエストが、どのメソッドで処理されるのかを指定します。


@GetMapping("/list")


これで、"/list"にGETリクエストがあったら、タスク情報を取得するメソッドはこの下で行うと指定することができました。
また、@GetMappingを記載することでHTTP通信に関する部分の実装をSpring Webに任せることができます。
その他、補足情報として@GetMappingは@RequestMappingのGETリクエスト用のアノテーションになります。


例えば、今回の@GetMapping("/list")は
@RequestMaping("/list", method=RequestMethod.GET)と記述したのと同じになります。
@GetMappingとすることで、記述の省略と可読性の向上が期待できます。


listItemsメソッドで返すのは、対応するビュー名の文字列になります。
対応するビュー名というのは、
このJavaのlistItemsメソッドの処理結果はどこのHTMLに埋め込むのか、そのHTML名です。
注意点としては、ビュー名は設定ファイルなどで別途指定していない場合には、
HTMLのテンプレートファイルから拡張子を除いたものになります。
今回のlistItemsメソッド場合、home.htmlを使うので「home」をビュー名として返します。


String listItems(Model model)


引数として、org.springframework.ui.Modelクラスのオブジェクトを受け取ります。
ModelクラスはJavaプログラムとHTMLテンプレートの間で値を受け渡す役割を担います。


model.addAttribute("taskList", listItems);


引数に渡されたModelオブジェクトに対しては、addAttributeメソッドを使って属性を設定できます。
属性はキーと値の組み合わせになっていて、キーはHTMLテンプレート側で使用している変数になります。

今回はhome.htmlに渡す属性として、
キーに"taskList"を、値にtaskItemsを設定しています。
こうすることで、home.htmlの${taskList}の部分が、
taskItemsの中身であるListオブジェクトに置き換わるということです。
なお、属性のキーと値の変数名は同じである必要はありません。
今回はあえて「taskList」と「taskItems」のように別のものにしてあります。


この状態でプロジェクトを再実行し、ブラウザでアクセスします。

http://localhost:8888/home
※ポート番号は自分で指定した値を使用する


入力フォームは表示されますが、まだタスクを1つも登録していないのでテーブルの中身は空です。