たなかのJava日記

どんなことをやったか(学んだか)、どこで詰まったか(わからなかったか)、どこで工夫したかの記録です。

【Spring Boot】CSSでの装飾について

前回までで、Webブラウザからタスクを登録し、一覧表示するという一連の動作が実装できました。
ただ、このままでは見た目が簡素です。
CSSを使ってWebページの見栄えを整えることができるので、そのやり方を残しておこうと思います。


■Spring BootでCSSの配置箇所
/src/main/resources/staticフォルダ配下に.cssの拡張子ファイルを配置

Spring BootでCSSや画像などブラウザから直接アクセスするファイルは、
/src/main/resources/staticフォルダ配下に配置する必要があります。


CSSの適用方法
適用するHTMLのheadタグ内にlinkタグの記述を追加します。

<head>
    <meta charset="UTF-8">
    <title>タスク管理アプリケーション</title>
    <link th:href="@{/home.css}" rel="stylesheet">
</head>


■反映されない場合の確認事項
・再実行したつもりになっていないか
cssのファイル名が間違っていないか
cssファイルの配置箇所がstaticフォルダではなくresourceフォルダになっていないか


これでWebページの見た目が整ったわけですが、問題点はまだあります。
せっかく登録したタスクの情報はJVMのメモリ内にしか記録されていないため、
一度アプリケーションを再起動するとすべて消えてしまいます。
アプリケーションが終了してもデータを消えないようにするためには、
外部にデータを保存しておくデータベースを使用しなければなりません。
なので、本日以降はデータベースについて学びます。

しかし、Spring Bootのように毎回まとめるのには膨大な時間がかかるので、
難解な箇所があった際に、脳内の整理整頓も兼ねてここに記す方向性でやっていきます。

Day40

■時間

3.5時間

 

■行ったこと

1タスクの一覧表示、HTMLとJavaの実装

2,タスク登録、HTMLとJavaの実装

3,エラーについてのまとめ

 

■出来たこと

1~3を実装し、理解度をチェックするためアウトプットを行いました。

また、昨日は何となくでHTMLを使用していましたが、調べて理解が出来ました。

 

■出来なかったこと

画面のCSSが完成していないので、明日取り組みたいです。

【Spring Boot】登録ボタン押下するとエラーになる件について

前回までで、タスクの一覧表示とタスクの登録ができるようになったのですが、
作成した直後、登録ボタンを押下するとエラーになってしまいました。


自分の思考のクセや、ミスをしがちなパターンかもしれないので
サンプルとして記しておきます。


今回作成したのが、hello.htmlとHomeControllerクラスになります。
この中から間違いを探しました。


<!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>
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";
    }

    @GetMapping("/add")
    String addItem(@RequestParam("task") String task,
                   @RequestParam("deadline") String deadline) {
        String id = UUID.randomUUID().toString().substring(0, 8);
        TaskItem item = new TaskItem(id, task, deadline, false);
        taskItems.add(item);

        return "redirect:/list";
    }
}


hello.htmlでは、
&{task.deadline}が間違っています。


正しくは、${task.deadline}です。
&のせいで変数と認識されていなかったわけです。


HomeControllerクラスでは、
private List taskItems = new ArrayList<>();
が間違っています。


正しくは、
private List taskItems = new ArrayList<>();
となります。


以前作成したクラスから同様のListをコピペで持ってきた際に、
コピー元のクラスを型に指定しまったようです。


こういった思い込みがバグの原因になるので気を付けなけばなりません。


追記
登録したタスクが正しく表示されない場合は、まずはHTMLテンプレートとの連携に使用している
属性のキーを間違えていないかを確認するのが良いとのことです。

今回の例だと、Java側で使用している属性のキー"taskList"と
HTMLテンプレート側で使用している${taskList}が一致していないことです。

【Spring Boot】タスクの機能追加のエンドポイントを実装する

前回までで登録ボタンを押してタスクを登録しようとしても、エラーになってしまいます。
HomeControllerクラスに、タスクを登録するためのエンドポイントが作られていないためです。

なので、タスクを登録するためのエンドポイントをHomeControllerクラスに追加します。

    @GetMapping("/add")
    String addItem(@RequestParam("task") String task,
                             @RequestParam("deadline") String deadline) {
        String id = UUID.randomUUID().toString().substring(0, 8);
        TaskItem item = new TaskItem(id, task, deadline, false);
        taskItems.add(item);

        return "redirect:/list";
    }


まず@GetMapping("/add")のaddItemメソッドは、
以下のhome.htmlの入力フォームのリクエスト先としてされています。

        <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>


メソッド内については、以前記事にしたので省略します。

【Spring Boot】@GetMapping、@RequestParamについて - たなかのJava日記


ただ、戻り値が大きく異なることに注意してください。


■listItemsメソッドの戻り値
return "home";
HTMLテンプレートの名称を返していた


■addItemメソッドの戻り値
return "redirect:/list";


"redirect:/〇〇"とした場合には、
○○の部分には表示するWebページを指定のパスにリダイレクトするという意味になります。
今回はパスの部分を"/list"としているので、listItemsメソッドのエンドポイントにリダイレクトされることを意味しているわけです。


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

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

タスクも登録できて反映されたら成功です。


ここまで追加してきたHomeControllerクラスは以下になります。

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 org.springframework.web.bind.annotation.RequestParam;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

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

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

    @GetMapping("/add")
    String addItem(@RequestParam("task") String task,
                   @RequestParam("deadline") String deadline) {
        String id = UUID.randomUUID().toString().substring(0, 8);
        TaskItem item = new TaskItem(id, task, deadline, false);
        taskItems.add(item);

        return "redirect:/list";
    }
}

【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つも登録していないのでテーブルの中身は空です。

【Spring Boot】タスクの追加、一覧表示用のHTMLを用意する

前回までで、HTMLとJavaを分けて構成しました。
その2つを繋ぐのがThymeleafといテンプレートエンジンです。

今回はタスクの追加機能と一覧機能の部分をThymeleafを使ってHTMLの箇所を作ります。
画面の構成は、下記を1ページ(1画面)にまとめたいと思います。

ファイル名「home.html」
・タスクを登録するための入力フォーム
・登録済みのタスクを一覧表示するテーブル

<!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で、タスク名と期限を入力できるようになっています。
また、登録ボタンをクリックすると、サーバー側の/addのパスに対してGETメソッドで入力内容を送信をします。


div class="task_form"
⇒囲ったタグは一つのかたまりになります。
また、classで名前を指定することにより、他のdivとの区別ができるようになります。


form
⇒入力&送信フォームを作成する際に使用する要素


form action=”リンク先”
⇒実行(サブミット)されたら指定されたリンク先に飛ぶ


label
⇒inputやtextareaなどの入力欄に対して説明をつけるために使います
主に「何を入力する欄なのか」が分かるように、テキストや画像を入れます
labelタグの使い方 - HTMLリファレンス


input name="task"
⇒name="task"により、そのinputタグの名前がtaskになります
サーバーへ送信したときに「task=掃除」のようにどの値が、どの入力欄に対応しているのか分かるようになります


input type="text"
⇒1行のテキスト入力欄を作るために使います
【HTML】inputタグの使い方・コンプリートガイド


input type="submit"
⇒フォームの実行ボタンを作成します


input value="登録"
⇒部品の初期値を設定(今回は登録というボタンが初期値になる)


■タスクを一覧表示するテーブルの部分について
プログラム内で生成したタスクに関する情報が、テンプレートエンジンによって挿入される箇所です。
そのため、2つの属性を使用しています。


th:each=”変数 : ${配列名}”
⇒今回の場合、${tasklist}の部分にプログラムからListオブジェクトが渡されると、
そのListに含まれている要素の数だけtrタグが生成されます(今回の要素の数は、タスク数になります)
Listそれぞれの要素のオブジェクトは、いったんtask変数に格納されます。Javaの拡張for文のような動きになります。


th:text
⇒この場合はtdタグの中身が属性値に指定された文字列に置き換わります。
属性値は${task.id}のような指定になっていますが、これはtaskオブジェクトのidフィールドの値を表します。


th:text="${task.done} ? '完了': '未完了'"
⇒Thymeleafでは、値の部分に条件式を書くことも可能です。Javaと同じ文法の3項演算子を使っています。
${task.done}がtrueであれば「:」の前の「完了」
${task.done}がfalseであれば「:」の後の「未完了」のいずれかの表示になります。


table border="1" style="border-collapse:collapse;"
⇒tableタグを使えば、表を作成することができます


⇒borderはテーブルに枠線を表示させることができます。
border属性に指定した数値が、テーブルの枠線の幅になります。
もし仮に、border属性を指定しなければ、枠線が消えます。


⇒border-collapse:collapseは枠線を1重線にしています。
特に何も指定しない限りは2重線になります。


thead
⇒ 表の中でヘッダー部分を表すタグです
theadタグとは?使い方やtbodyタグとtfootタグとの違いを解説 | ウェブカツBLOG


tbody
⇒テーブルの本体部分を表すタグです
HTMLのテーブルで使われるtbodyタグについて紹介 | ウェブカツBLOG


tr
⇒表の行を定義するための要素です
HTML tableタグを初心者向けに解説&便利な知識…|Udemy メディア


td
⇒表のデータを入れるための要素です
HTML tableタグを初心者向けに解説&便利な知識…|Udemy メディア

Day39

■時間

1.0時間

 

■行ったこと

1,タスクの追加機能と一覧機能の部分をThymeleafを使用し作成

2,HTMLについて

 

■出来たこと

使用したSpring Bootのアノテーションについて

Webアプリケーションの仕組みについて

 

■出来なかったこと

HTMLのタグがぱっと見わからないのがいくつかありました。

画面の骨組みでもあるので、適切なタグや属性を選択できるようになりたいです。