SpringBootでToDoアプリを作ってみよう【+jQuery】

#はじめに
SpringBootを使ってTodoアプリを作っていきます。

↓以下の記事の続きです。ここに加えていくので、先に作ってみてください。
[SpringBootでToDoアプリを作ってみよう【誰でも作れます・初心者向け】](https://qiita.com/toki_k/items/f9fcdf7d65f3a8ab0f23)

前回分のソースファイル
https://github.com/tokio-k/TodoApp-springboot/tree/intermediate

前回まででこんな状態です。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506867/d426cbc3-ef04-0d26-a899-03cefc9028e6.png)

これから、SpringBootの勉強を始めるという人の役に立てれば、いいなと思っています。
基本的にコードはファイル全部を載せるようにします。自分が勉強している時に、書く場所がわからないことがあったので。
既存のものを少し編集するだけの時は省略します。
Ajaxを使った非同期処理で通信を行っていきます。
SpringBootとjQueryのデータ送受信がメインになるかと思います。

#使う技術

* テンプレートエンジン　Thymeleaf
* データベース　PostgreSQL
* ORマッパー　　Mybatis
* フロント部分　jQuery 、Bootstrap

#流れ

* CSS・JavaScriptを使う準備
* タスク一覧表示の改修
* 更新処理の改修
* 完了済みの表示表示機能の追加
* 追加機能の表示&追加処理の改修
* 削除機能の表示&削除処理の改修

#CSS・JavaScriptを使う準備
Thymeleafのみだったフロンド部分に、CSSやJavaScriptを追加していきます。
src/main/resources/staticの下に記述する必要があります。

src/main/resources/staticの下に
「css」フォルダと「js」フォルダを作成します。
この中に、作成したCSSファイルやJsファイルを格納してくことにします。
※今回cssファイルはほとんど使わないです。学習用に1つだけ使います。

cssフォルダには、「style.css」を
jsフォルダには、「todo.js」を作成します。

index.htmlのheadの中に以下を追加します。(少し下にindex.html全体を載せてます。)

```html
<link rel="stylesheet" th:href="@{/css/style.css}" />
```
index.htmlのbodyの最後に以下を追加します。

```html
<script type="text/javascript" th:src="@{/js/todo.js}"></script>
```
これで、cssフォルダとJsフォルダの中のファイルを適応できます。

今回は、jQueryとBootstrapも使用するので、pom.xmlに以下を追記します。

```pom.xml

        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>bootstrap</artifactId>
            <version>4.6.0</version>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>3.5.1</version>
        </dependency>
```
([webjars](https://www.webjars.org/)で依存追加の書き方を確認できます。)
dependenciesタグの中に追加します。

index.htmlのheadの中に以下を追加します。

```html
<script src="webjars/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="webjars/bootstrap/4.6.0/css/bootstrap.min.css" />
<script src="webjars/bootstrap/4.6.0/js/bootstrap.min.js"></script>
```
これで、jQueryとBootstrapが使えます。


#タスク一覧表示の改修

画面を少し整えていきます。

__index.htmlを編集します__
今回は、formの送信ではなくjQueryで情報を取得して、Controller側に渡すことにします。


```index.html
<!DOCTYPE html>
<html  xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<script src="webjars/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="webjars/bootstrap/4.6.0/css/bootstrap.min.css" />
<script src="webjars/bootstrap/4.6.0/js/bootstrap.min.js"></script>
<link rel="stylesheet" th:href="@{/css/style.css}" />
<title>TodoApp</title>
</head>
<body>
  <h1>TodoList</h1>
  <h3>マイタスク</h3>
  <!--↓編集箇所  -->

  <table class="table table-borderless">
    <thead>
    </thead>
    <tbody id="todes">
      <tr class="todo" th:each="todo : ${todos}">
        <td style="width:2rem">
          <input type="checkbox"name="done_flg"/>
          <input type="hidden" name="id" th:value="${todo.id}" />
        </td>
        <td  style="width:22rem"><input type="text" style="border:none;width:22rem" name="title" th:value="${todo.title}"/></td>
        <td><input type="date" style="border:none" name="time_limit" th:value="${todo.time_limit}" /></td>
      </tr>
    </tbody>
  </table>

  <div style="font-size:20px">
    <span id="done_num">1</span>
    <span class="pr-3">件完了</span>
    <span class="button_for_show" style="display:inline-block"></span>
  </div>
  <table class="table table-borderless">
  <thead>
  </thead>
    <tbody id="donetodes">
      <tr class="todo" th:each="todo : ${doneTodos}">
        <td style="width:2rem">
          <input type="checkbox"name="done_flg" checked/>
          <input type="hidden" name="id" th:value="${todo.id}" />
        </td>
        <td  style="width:22rem"><input type="text" style="border:none;width:22rem;text-decoration:line-through" name="title" th:value="${todo.title}"/></td>
        <td><input type="date"style="border:none;display:none" name="time_limit" th:value="${todo.time_limit}" /></td>
      </tr>
    </tbody>
  </table>

  <!--↑編集箇所  -->

  <h3>新しいタスクを追加</h3>
  <form method="post" th:action="@{/add}">
    <input type="text" name="title" />
    <input type="date" name="time_limit"/>
    <input type="submit" value="追加" />
  </form>

  <form method="post" th:action="@{/delete}">
    <input type="submit" value="完了済みを削除" />
  </form>

  <script type="text/javascript" th:src="@{/js/todo.js}"></script>
</body>
</html>
```
cssも1つだけ

```style.css
.button_for_show {
	border-right : solid 2px #000;
	border-bottom : solid 2px #000;
	width:1rem;
	height:1rem;
	transform : rotate(45deg);
	position:relative;
	bottom:4px
}
```
こんな感じになりました。
jsで使うために名前がついてたりしますが、今は気にしないでください。
見出しなど自由にカスタマイズしてみてください。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506867/0e9d77ee-cdb4-283b-df45-c9aaf6f5eeba.png)
※写真イメージには、\<span id="done_num">2\</span>と直接2を書いています。
※上のコードでは2は表示されません。今からJsで実装します

完了件数の数を取得して表示します。
__todo.jsを編集します__

\<tbody id="donetodes">の子要素の数を数えて表示しています

```index.js
/**
 *
 */
$(function(){

//完了済みの個数取得・表示
  let doneCount = $("#donetodes").children("tr").length;
  $("#done_count").text(doneCount);

})
```

#更新処理の改修
index.htmlを編集したので、それに合わせて更新をできるようにしていきます。
編集をしたらすぐに更新できるようにします。

__todo.jsを編集します__

ここでやることは以下です。

* 更新があるたびに処理を実行する
* 1つのタスクの情報(id,title,time_limit,done_flg)を取得する
* 取得した値をController側に送って/updateの処理を実行する
* 完了(未完了)ボタンを押した際の処理を実行する
 * 完了済み(未完了)へ移動する
 * 文字に打ち消し線をつける(消す)
 * 日付を隠す(表示する)
 * 完了件数を更新する

```todo.js
$(function(){

//更新処理
$('.todo input').change(function(){
	const todo = $(this).parents('.todo');
	const id = todo.find('input[name="id"]').val();
	const title = todo.find('input[name="title"]').val();
	const time_limit = todo.find('input[name="time_limit"]').val();
	const is_done = todo.find('input[name="done_flg"]').prop("checked");
	let done_flg;
	if(is_done == true) {
	  done_flg = 1;
	}else{
	  done_flg = 0;
	}

	const params = {
		id : id,
		title : title,
		time_limit : time_limit,
		done_flg : done_flg
	}
	$.post("/update",params);

    //完了ボタンを押した際の処理
    doneCount =  $("#done_count").text();

	if($(this).prop('name') == "done_flg"){
	  if(isDone == true){
	    $(todo).appendTo("#donetodes");
	    todo.find('input[name="title"]').css('text-decoration','line-through')
	    todo.find('input[name="time_limit"]').hide();
	    doneCount ++;
	  }else{
	    $(todo).appendTo("#todes");
	    todo.find('input[name="title"]').css('text-decoration','none')
	    todo.find('input[name="time_limit"]').show()
	    doneCount --;
	  }

	  $("#done_count").text(doneCount);
	}

})

})
```
1つ目のconstで\<tr>単位の要素(todo単位)を取得しています。
2~5つ目のconstでそれぞれのinputの要素を取得しています。
5つ目のconstでそれぞれの値をオブジェクトに格納し、その後postメソッドでcontrollerに送っています。
※postメソッドに続けて失敗時の処理、controllerからの戻り値を扱う処理等も書くことができます。
※ajaxやpostメソッドなどで調べてみてください。

「完了(未完了)ボタンを押した際の処理」以下では、以下の処理を記載しています。

* 完了(未完了)ボタンを押したかどうかで条件分岐
* 完了(未完了)ボタンを押しており完了済みの場合の処理を記載
* 完了(未完了)ボタンを押しており未完了の場合の処理を記載

これで更新ができるようになりました。

__TodoController.javaを編集する__
非同期処理で画面遷移もなくしているので、Controllerのupdateメソッドも以下のように変更します。

```TodoController.java
	@RequestMapping(value="/update")
	@ResponseBody
	public void update(Todo todo) {
		todoMapper.update(todo);
	}
```
Controllerはビュークラスを返すのが基本ですが、＠ResponseBodyをつけることでコンテンツが返せます。
今回は、何も返していないのですが、つけなかったらエラーになったのでつけておきます。

<details>
<summary>__【折り畳み(本文とは関係のない内容)】__
フォームの入力チェックでのエラー内容や、更新処理の成功かどうかのデータ等を返すこともあります。</summary><div>

更新処理のエラーかどうか返す例

```(例)Controller.java
	@RequestMapping(value="/update")
	@ResponseBody
	public String update(Entity entity) {
		String succes = false;
		try {
		    Mapper.update(Entity);
		    succes = "true";
		} catch(Exception e) {
		    succes = "false";
		}
		return succes;
	}
```
Mapなどを使って、内容を返したりなどもできます。
String→Map\<String,Object> 、 Stringに"errMsg",Objectにエラーメッセージのリスト等
この戻り値はjQueryのpostメソッドに連結させたメソッドの引数となります。

</div></details>

#完了済みの表示非表示機能の追加

完了しているものは基本的に消しておいて必要な時のみ表示できるようにします。

__index.htmlを編集する__

2つ目のtableタグ(完了済みを表示している方)にstyleとidをつけます。
※変更がここだけなので、他省略しました。

```index.html
<table class="table table-borderless" style="display:none"id="done_table">
```
これで、完了済みのタスクが表示されなくなりました。

__todo.jsを編集する__

「完了済みタスク表示/非表示切り替え」以降が編集箇所です。
showStateには、done_tableのdisplayプロパティの値を格納しています。
displayプロパティがnoneの場合は、表示させるための処理を
displayプロパティがnone以外の場合は、非表示にするための処理を書きます
cssを変更して、ボタンの向きと位置も変更しています。

```todo.js
$(function(){

//完了済みの個数取得・表示
  let doneCount = $("#donetodes").children("tr").length;
  $("#done_count").text(doneCount);

//更新処理
$('.todo input').change(function(){
	const todo = $(this).parents('.todo');
	const id = todo.find('input[name="id"]');
	const title = todo.find('input[name="title"]');
	const timeLimit = todo.find('input[name="time_limit"]');
	const isDone = todo.find('input[name="done_flg"]').prop("checked");
	let doneFlg;
	if(isDone == true) {
	  doneFlg = 1;
	}else{
	  doneFlg = 0;
	}

	const params = {
		id : id.val(),
		title : title.val(),
		time_limit : timeLimit.val(),
		done_flg : doneFlg
	}
	$.post("/update",params);

    //完了ボタンを押した際の処理
    doneCount =  $("#done_count").text();

	if($(this).prop('name') == "done_flg"){
	  if(isDone == true){
	    $(todo).appendTo("#donetodes");
	    todo.find('input[name="title"]').css('text-decoration','line-through')
	    todo.find('input[name="time_limit"]').hide();
	    doneCount ++;
	  }else{
	    $(todo).appendTo("#todes");
	    todo.find('input[name="title"]').css('text-decoration','none')
	    todo.find('input[name="time_limit"]').show()
	    doneCount --;
	  }

	  $("#done_count").text(doneCount);
	}


})

//完了済みタスク表示/非表示切り替え
$('.button_for_show').click(function(){
    let showState = $('#done_table').css('display');
    if(showState == "none") {
        $('#done_table').show();
        $(this).css({ transform: ' rotate(225deg)','bottom':'-4px' });
    }else{
        $('#done_table').hide();
        $(this).css({ transform: ' rotate(45deg)','bottom':'4px' });
    }
})

})
```
これで、完了済みタスクの表示/非表示の切り替えができるようになりました。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506867/d52ca9ff-ed40-9ce3-2267-5b1ecd3d2a55.png)
#追加機能の表示と追加処理の改修

__index.htmlを編集する__
追加機能のフォームには、BootStrapのモーダルを使用します。

モーダルは以下のようにすることで作ることができます。

```html
<button data-toggle="modal" data-target="#modal">
  モーダルを開くボタン
</button>
<div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="basicModal" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h3 class="modal-title" id="modalLabelId">タイトル</h3>
            </div>
            <div class="modal-body">
            　　<!--ここにbody要素を入れる-->
            </div>
            <div class="modal-footer">
                <!--ここにfooter要素を入れる-->
            </div>
        </div>
    </div>
</div>
```
参考：https://www.fenet.jp/dotnet/column/language/6549/
modal-headerには、タイトルなどのヘッダー要素、
modal-bodyには、内容となるボディ要素、
modal-footerには、ボタンなどのフッター要素、を入れます。

今回は、ボディにフォームと追加ボタンを作ります。
タイトルとフッダーは無しにします。
add_formの値をjQueryで取得して、Controller側に送ります。

```index.html
<!DOCTYPE html>
<html  xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<script src="webjars/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="webjars/bootstrap/4.6.0/css/bootstrap.min.css" />
<script src="webjars/bootstrap/4.6.0/js/bootstrap.min.js"></script>
<link rel="stylesheet" th:href="@{/css/style.css}" />
<title>TodoApp</title>
</head>
<body>
  <h1>TodoList</h1>
  <h3>マイタスク</h3>
  <table class="table table-borderless">
    <thead>
    </thead>
    <tbody id="todes">
      <tr class="todo" th:each="todo : ${todos}">
        <td style="width:2rem">
          <input type="checkbox"name="done_flg"/>
          <input type="hidden" name="id" th:value="${todo.id}" />
        </td>
        <td  style="width:22rem"><input type="text" style="border:none;width:22rem" name="title" th:value="${todo.title}"/></td>
        <td><input type="date" style="border:none" name="time_limit" th:value="${todo.time_limit}" /></td>
      </tr>
    </tbody>
  </table>

  <div style="font-size:20px">
    <span id="done_count"></span>
    <span class="pr-3">件完了</span>
    <span class="button_for_show " style="display:inline-block"></span>
  </div>
  <table class="table table-borderless" style="display:none"id="done_table">
  <thead>
  </thead>
    <tbody id="donetodes">
      <tr class="todo" th:each="todo : ${doneTodos}">
        <td style="width:2rem">
          <input type="checkbox"name="done_flg" checked/>
          <input type="hidden" name="id" th:value="${todo.id}" />
        </td>
        <td  style="width:22rem"><input type="text" style="border:none;width:22rem;text-decoration:line-through" name="title" th:value="${todo.title}"/></td>
        <td><input type="date"style="border:none;display:none" name="time_limit" th:value="${todo.time_limit}" /></td>
      </tr>
    </tbody>
  </table>

  <!--↓編集箇所  -->

   <button type="button" class="btn btn-light rounded-circle p-0 text-muted font-weight-bold" data-toggle="modal"data-target="#modal"
    style="width:2.5rem;height:2.5rem;">＋</button>
   <div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="basicModal" aria-hidden="true">
       <div class="modal-dialog">
           <div class="modal-content">
               <div class="modal-body">
                   <form id="add_form">
                       <div class="form-group">
                           <input type="text" name="title" class="form-control"placeholder="新しいタスク"/>
                       </div>
                       <div class="form-group">
                           <input type="date" name="time_limit"class="form-control" style="width:60%;" />
                       </div>
                       <button type="button" class="btn btn-primary float-right " data-dismiss="modal" id="add">追加</button>
                   </form>
               </div>
           </div>
       </div>
   </div>

  <!--↑編集箇所  -->


  <form method="post" th:action="@{/delete}">
    <input type="submit" value="完了済みを削除" />
  </form>

  <script type="text/javascript" th:src="@{/js/todo.js}"></script>
</body>
</html>

```

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506867/e76a47cf-6ee2-8e8c-642b-e5f73ec5cf57.png)

+ボタンを押すと、こんな感じになります。


__TodoController.javaを編集する__

非同期処理にあわせてControllerを編集していきます。
引数としてTodoのタイトルと期間をわたし、データベースに登録
戻り値として、引数のデータに対応するidを加えたデータを返します。

idはデータベース登録時に自動で値が登録されるものなので、その値を取得する必要があります。

```TodoController.java
	@RequestMapping(value="/add")
	@ResponseBody
	public Todo add(Todo todo) {
		todoMapper.add(todo);
		return todo;
	}
```
todoMapper.addを実行するとtodoにidが自動で追加されるように、
TodoMapper.xmlを編集していきます。

__TodoMapper.xmlを編集する__
insert時に自動採番されたidを取得できるようにしていきます。
この時、idは戻り値ではなく、引数に渡したオブジェクトにマッピングされます。

つまり、戻り値などを受け取りセットする必要もなく、自動でtodoに追加されます。
(戻り値は更新件数になります。ここでは1行insertなので1)

```TodoMapper.xml
<insert id="add"  useGeneratedKeys="true" keyProperty="id" parameterType="com.todo.app.entity.Todo">
    insert into todo_items (title,time_limit)
    values (#{title},to_date(#{time_limit},'yy-mm-dd'))
  </insert>
```
addメソッドの実装をしている、このinsert文に「useGeneratedKeys="true" keyProperty="id"」を設定します。
これで、自動採番された値をidに入れることができるようになりました。(keyPropertyの値はフィールド名)

__todo.jsを編集する__

追加ボタンを押してから、Controllerとデータのやり取りをし、表示するまでを実装していきます。

```todo.js
$(function(){

//完了済みの個数取得・表示
  let doneCount = $('#donetodes').children("tr").length;
  $('#done_count').text(doneCount);

//更新処理
$('.todo input').change(function(){
	const todo = $(this).parents('.todo');
	const id = todo.find('input[name="id"]');
	const title = todo.find('input[name="title"]');
	const timeLimit = todo.find('input[name="time_limit"]');
	const isDone = todo.find('input[name="done_flg"]').prop("checked");
	let doneFlg;
	if(isDone == true) {
	  doneFlg = 1;
	}else{
	  doneFlg = 0;
	}

	const params = {
		id : id.val(),
		title : title.val(),
		time_limit : timeLimit.val(),
		done_flg : doneFlg
	}
	$.post("/update",params);

    //完了ボタンを押した際の処理
    doneCount =  $('#done_count').text();

	if($(this).prop('name') == "done_flg"){
	  if(isDone == true){
	    $(todo).appendTo('#donetodes');
	    todo.find('input[name="title"]').css('text-decoration','line-through')
	    todo.find('input[name="time_limit"]').hide();
	    doneCount ++;
	  }else{
	    $(todo).appendTo('#todes');
	    todo.find('input[name="title"]').css('text-decoration','none')
	    todo.find('input[name="time_limit"]').show()
	    doneCount --;
	  }

	  $("#done_count").text(doneCount);
	}

})

//完了済みタスク表示/非表示切り替え
$('.button_for_show').click(function(){
    let showState = $('#done_table').css('display');
    if(showState == "none") {
        $('#done_table').show();
        $(this).css({ transform: ' rotate(225deg)','bottom':'-4px' });
    }else{
        $('#done_table').hide();
        $(this).css({ transform: ' rotate(45deg)','bottom':'4px' });
    }
})

//追加処理
$('#add').click(function() {
    const params = $('#add_form').serializeArray();
    $.post("/add",params).done(function(json){
        const clone = $('#todes tr:first').clone(true);
        clone.find('input[name="id"]').val(json.id);
        clone.find('input[name="title"]').val(json.title);
        clone.find('input[name="time_limit"]').val(json.time_limit);
        $('#todes').append(clone[0]);
    })
})


})

```
追加処理でやっていることは以下です。

* serializeArray()でformの値を取得
* 取得した値をControllerに送る
* Controllerから値を取得
* タスク一覧の1つ目の要素をコピー
* コピーした要素の値をコントローラーから取得した値に変更
* タスク一覧の最後に追加

これで、非同期にデータを追加することができるようになりました。

#削除機能の表示&削除処理の改修

最後に削除ボタンも画面遷移なしで実行できるようにしていきます。

__index.htmlを編集する__
「完了済みを削除」をボタンにするだけです。
ほとんど変更はありません。

```index.html
<!DOCTYPE html>
<html  xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<script src="webjars/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="webjars/bootstrap/4.6.0/css/bootstrap.min.css" />
<script src="webjars/bootstrap/4.6.0/js/bootstrap.min.js"></script>
<link rel="stylesheet" th:href="@{/css/style.css}" />
<title>TodoApp</title>
</head>
<body>
  <h1>TodoList</h1>
  <h3>マイタスク</h3>
  <table class="table table-borderless">
    <thead>
    </thead>
    <tbody id="todes">
      <tr class="todo" th:each="todo : ${todos}">
        <td style="width:2rem">
          <input type="checkbox"name="done_flg"/>
          <input type="hidden" name="id" th:value="${todo.id}" />
        </td>
        <td  style="width:22rem"><input type="text" style="border:none;width:22rem" name="title" th:value="${todo.title}"/></td>
        <td><input type="date" style="border:none" name="time_limit" th:value="${todo.time_limit}" /></td>
      </tr>
    </tbody>
  </table>

  <div style="font-size:20px">
    <span id="done_count"></span>
    <span class="pr-3">件完了</span>
    <span class="button_for_show " style="display:inline-block"></span>
  </div>
  <table class="table table-borderless" style="display:none"id="done_table">
  <thead>
  </thead>
    <tbody id="donetodes">
      <tr class="todo" th:each="todo : ${doneTodos}">
        <td style="width:2rem">
          <input type="checkbox"name="done_flg" checked/>
          <input type="hidden" name="id" th:value="${todo.id}" />
        </td>
        <td  style="width:22rem"><input type="text" style="border:none;width:22rem;text-decoration:line-through" name="title" th:value="${todo.title}"/></td>
        <td><input type="date"style="border:none;display:none" name="time_limit" th:value="${todo.time_limit}" /></td>
      </tr>
    </tbody>
  </table>


   <button type="button" class="btn btn-light rounded-circle p-0 text-muted font-weight-bold" data-toggle="modal"data-target="#modal"
    style="width:2.5rem;height:2.5rem;">＋</button>
   <div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="basicModal" aria-hidden="true">
       <div class="modal-dialog">
           <div class="modal-content">
               <div class="modal-body">
                   <form id="add_form">
                       <div class="form-group">
                           <input type="text" name="title" class="form-control"placeholder="新しいタスク"/>
                       </div>
                       <div class="form-group">
                           <input type="date" name="time_limit"class="form-control" style="width:60%;" />
                       </div>
                       <button type="button" class="btn btn-primary float-right"  data-dismiss="modal" id="add">追加</button>
                   </form>
               </div>
           </div>
       </div>
   </div>
  <!--↓編集箇所  -->
  <button type="button" class="btn btn-outline-secondary" id="delete">完了済みを削除</button>
  <!--↑編集箇所  -->
  <script type="text/javascript" th:src="@{/js/todo.js}"></script>
</body>
</html>

```
__TodoController.javaを編集する__

```TodoController.java
	@RequestMapping(value="/delete")
	@ResponseBody
	public void delete() {
		todoMapper.delete();
	}
```

__todo.jsを編集する__

行う事は以下です。

* Controllerのdeleteメソッドの実行
* 完了済みタスクの表示削除
* 完了件数のリセット

```todo.js
$(function(){

//完了済みの個数取得・表示
  let doneCount = $('#donetodes').children("tr").length;
  $('#done_count').text(doneCount);

//更新処理
$('.todo input').change(function(){
	const todo = $(this).parents('.todo');
	const id = todo.find('input[name="id"]');
	const title = todo.find('input[name="title"]');
	const timeLimit = todo.find('input[name="time_limit"]');
	const isDone = todo.find('input[name="done_flg"]').prop("checked");
	let doneFlg;
	if(isDone == true) {
	  doneFlg = 1;
	}else{
	  doneFlg = 0;
	}

	const params = {
		id : id.val(),
		title : title.val(),
		time_limit : timeLimit.val(),
		done_flg : doneFlg
	}
	$.post("/update",params);

    //完了ボタンを押した際の処理
    doneCount =  $('#done_count').text();

	if($(this).prop('name') == "done_flg"){
	  if(isDone == true){
	    $(todo).appendTo('#donetodes');
	    todo.find('input[name="title"]').css('text-decoration','line-through')
	    todo.find('input[name="time_limit"]').hide();
	    doneCount ++;
	  }else{
	    $(todo).appendTo('#todes');
	    todo.find('input[name="title"]').css('text-decoration','none')
	    todo.find('input[name="time_limit"]').show()
	    doneCount --;
	  }

	  $("#done_count").text(doneCount);
	}


})

//完了済みタスク表示/非表示切り替え
$('.button_for_show').click(function(){
    let showState = $('#done_table').css('display');
    if(showState == "none") {
        $('#done_table').show();
        $(this).css({ transform: ' rotate(225deg)','bottom':'-4px' });
    }else{
        $('#done_table').hide();
        $(this).css({ transform: ' rotate(45deg)','bottom':'4px' });
    }
})

//追加処理
$('#add').click(function() {
    const params = $('#add_form').serializeArray();
    $.post("/add",params).done(function(json){
        const clone = $('#todes tr:first').clone(true);
        clone.find('input[name="id"]').val(json.id);
        clone.find('input[name="title"]').val(json.title);
        clone.find('input[name="time_limit"]').val(json.time_limit);
        $('#todes').append(clone[0]);
    })
})

//削除処理
$('#delete').click(function(){
    $.post("/delete").done(function(){
        $('#donetodes').empty();
        $('#done_count').text(0);
    })
})

})
```
これで削除もできました。

#まとめ

画面遷移なしで、SpringBootとフロント側でデータの送受信を行えるように実装していきました。

それぞれの要素がすべて左寄席1列で並んでいるのでカスタマイズしたり、
例外処理やバリデーションチェック等の機能も実装してみたり、
色々と遊んでみてください。

1から作るよりも、何かを改修していく方がハードルが低いと思っています。

「SpringBootの勉強を始めた！」という人の役に立てればいいなと思ってます。
思ったより長くなってしまいました。
慣れないながらも書くの頑張ったので、LGTMもよかったら押してください。
めっちゃ喜びます。

↓ソースファイルです
https://github.com/tokio-k/TodoApp-springboot

#参考文献
https://www.fenet.jp/dotnet/column/language/6549/
https://qiita.com/fukasawah/items/eb0f7f067f8b347cbb2a
https://getbootstrap.jp/docs/4.2/getting-started/introduction/
https://www.webjars.org/