10月13日

EclipseのTomcatのエラーをコンソールに出力する方法

EclipseからTomcatを起動しようとすると、「重大: 以前のエラーのためにコンテキストの起動が失敗しました」のような簡単なメッセージだけ表示して起動できないことがある。

Tomcatが起動しないときは、コンソールにエラーの内容を出力して原因を調べるのが良い。

コンソールにログを出力するには、以下の設定を行う。

プロジェクト・エクスプローラのServerにある「ローカルホストのTomcat8…」を右クリックし、[新規]-[ファイル]を選択する。
ファイル名を「logging.properties」とする。
作成した logging.properties ファイルをダブルクリックして編集する。
以下の内容を記述する。

handlers = java.util.logging.ConsoleHandler

java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

「ローカルホストのTomcat8…」を右クリックし、[プロパティー]を選択する。
ロケーションが表示されているので全体をコピーする。(例: C:\Users\…Tomcat8-config)

「ローカルホストのTomcat8…」を右クリックし、[実行]-[実行の構成]を選択する。
「引数」タブを選択し、「VM引数」の最後に以下の内容を追加する。

-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file="(ロケーションに表示されていたパス)/logging.properties"

設定を「適用」してTomcatを再起動すれば、コンソールにエラーの内容が表示される。

JSPにCSSやJavaScriptファイルを読み込む方法

まずCSSやJavaScriptを保存するためのフォルダをwebappの下に追加する。

外部ファイルを読み込むための設定を mvc-config.xml に追加する。

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="jp.abc"/>


    <mvc:annotation-driven />

	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	        <!-- Example: a logical view name of 'showMessage' is mapped to '/WEB-INF/jsp/showMessage.jsp' -->
	        <property name="prefix" value="/WEB-INF/view/"/>
	        <property name="suffix" value=".jsp"/>
	</bean>

	<mvc:resources mapping="/css/**" location="/css/" />
	<mvc:resources mapping="/js/**" location="/js/" />

</beans>

JSPファイルから読み込むには以下のように記述する。

<!DOCTYPE html>

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>

<html>
<head>
<meta charset="utf-8">
<title>サンプルのWebアプリですよ</title>
<link rel="stylesheet" type="text/css" href="main.css" />
<script type="text/javascript" src="sample.js"></script>
</head>

<body>
<h1>サンプルのWebアプリですよ</h1>
</body>
</html>

10月6日

課題について補足

件名
件名は「何」に対して「どうする」のかを書くこと。ロバストネス図内のView(JSPファイル)・Controller(Javaファイル)・Entity(Javaファイル)や、設定ファイル(XMLファイル)、プロジェクトの内容や詳細を説明するWikiページ、画面に表示する画像ファイル、CSSファイル、JavaScriptファイルなどが「何」にあたる。

詳細
件名だけでわかるようなら詳細は書かなくてもよい。

コメント
Gitにコミット&プッシュする場合は、コミットのコメントに課題IDだけを書く。
自動的に、その課題のコメントに、GitのコミットIDが追加され、どのファイルが変更されたのかがわかるようになる。
Gitにコミット&プッシュしない変更を行った場合は、Wikiページはファイルなどへのリンクをコメントに貼って、その課題による成果がわかるようにすること。

10月2日

これからのチーム開発のやりかた

  1. 毎朝、それぞれが取り組む課題を決定する。
  2. Backlogに課題を登録する。
  3. 課題を「未対応」→「処理中」に変更する。
  4. 課題に取り組む。
  5. ファイルを新規で作った場合はGitに追加する。
  6. 終わったらGitにコミットする(コミットメッセージは課題のIDのみ)
  7. 課題を「処理中」→「処理済み」に変更する。
  8. チームのほかのメンバーに確認してもらう。
  9. 確認したメンバーが課題を「処理済み」→「完了」に変更する。
  10. Backlogのリモートリポジトリにプッシュする。
  11. ほかのメンバーにプッシュしたことを伝え、プルしてもらう。
  12. 1に戻る。

課題の内容は、当日(1~2現の3時間)で完了できる内容にすること。

課題のタイトルは、ロバストネス図に書いた、画面・コントローラ・エンティティなどを書いて、それに対してどのような変更をするのかを記載し、課題で何を解決しようとしているのかが一目でわかるようにすること。

課題の詳細欄には、変更する具体的な内容や、変更するファイル名などを記載する。タイトルだけでわかるのであれば、書かなくてもよい。

9月29日

リーダーが新規でプロジェクトを作成する。
「click to enter」を表示するまでの動作を確認する。
[チーム]-[プロジェクトの共用]を選択する。
リポジトリタイプは「Git」を選択する。
適当な場所にリポジトリを作成して「完了」をクリックする。

プロジェクトホームに「.gitignore」ファイルを作成する。

/target
.settings/
.classpath
/pleiades45

[チーム]-[コミット]でファイルをリポジトリに登録する。
不要なファイルが含まれていないか確認して、含まれていれば「.gitignore」ファイルに設定を追加する。
問題なければコミットを実行する。

[チーム]-[リモート]-[プッシュ]を選択し、BacklogのGitにあるリモートリポジトリにプッシュする。
プッシュ先のURLは、BacklogのGitのURLをコピーして使用する。

他のメンバーは、[新規]-[インポート]で、プロジェクトをBacklogのリモートリポジトリからインポートする。
インポートソースの選択で「Gitからプロジェクト」を選択し「次へ」をクリック。
「URLの複製」を選択して「次へ」をクリック。
BacklogのGitのURLをコピーしてURIにペーストし、BacklogのユーザーIDとパスワードを入力して「次へ」をクリック。
「master」が選択されていることを確認して「次へ」をクリック。
ローカルリポジトリのディレクトリーを指定して「次へ」をクリック。
プロジェクトをインポートするためのウィザードで「Import existing Eclipse project」を選択し「次へ」をクリック。
インポートしたいプロジェクトにチェックが入っていることを確認して「完了」をクリック。

あとはこれまでと同じように
[Maven]-[プロジェクトの更新]
[実行]-[maven install」
を実行すれば、ローカルでもWebアプリが動作するようになる。

9月25日

Backlogにログインする。
・課題を新規作成する。(Wikiページの作成)
・課題を処理中にする。
・Wikiページを作成する。
・課題を処理済みにする。
・他のメンバーに確認してもらったら、課題を完了にする。

9月15日

SpringMVCにおけるユーザー認証

SpringMVCでは、spring-securityを使ってユーザー認証を行う。
そのために、pom.xmlに以下の内容を追加する。

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core</artifactId>
    <version>4.0.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>4.0.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>4.0.1.RELEASE</version>
</dependency>

web.xmlの最後にフィルターを追加する。

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
  
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

src/main/resources/spring フォルダに、spring-security.xml を作成する。

<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security-4.0.xsd">
  
    <http pattern="/" security="none"/>
    <http pattern="/index.jsp" security="none"/>
    <http auto-config="true" >
        <intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
        <form-login />
        <logout />
    </http>
  
    <beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <beans:property name="driverClassName" value="org.hsqldb.jdbc.JDBCDriver" />
        <beans:property name="url" value="jdbc:hsqldb:hsql://localhost/mydata" />
        <beans:property name="username" value="sa" />
        <beans:property name="password" value="" />
    </beans:bean>
  
    <!-- ユーザとROLEを定義 -->
    <authentication-manager>
        <authentication-provider>
            <jdbc-user-service data-source-ref="dataSource"
                 users-by-username-query="SELECT user_name, password, enabled FROM users WHERE user_name = ?"
                 authorities-by-username-query="SELECT user_name, role FROM users WHERE user_name = ?" />
        </authentication-provider>
    </authentication-manager>
</beans:beans>

ユーザーとユーザの権限を管理するためのテーブルを作成する。

CREATE TABLE users(
 user_id bigint NOT NULL identity,
 user_name varchar(50) NOT NULL,
 password varchar(50) NOT NULL,
 enabled tinyint NOT NULL,
 role varchar(100) NOT NULL
);

USERSテーブルにテスト用のユーザーを登録する。

INSERT INTO users ( user_name , password , enabled, role)
   VALUES ( 'taro', 'abcd' , 1, 'ROLE_USER' );

チーム開発

チームで開発するWebアプリケーションを決めよう!

  • 2ちゃんねる
  • Twitter
  • LINE
  • Wiki
  • EC(ネットショップ)

以下のことを決めよう。

  • 作成するサービスを決める(候補を出す)
  • チーム名を決める
  • リーダーを決める
  • サービスの名前(プロジェクト名)を決める
  • 基本的な画面遷移図(ロバストネス図)を作成する

ロバストネス図は、astah community で作れる。

チーム1

チーム名:やりたい
リーダー:西田
サービス名:やりたま
プロジェクト名:Yaritama

チーム2

チーム名:ナイトプール
リーダー:中尾
サービス名: 写真共有アプリ
プロジェクト名: PhotoSharing

チーム3

チーム名:ボビーオロゴン
リーダー:前田
サービス名:診断メーカー
プロジェクト名:ShindanMaker

チーム4

チーム名:ママ、早く!
リーダー:山本
サービス名:掲示板
プロジェクト名:3chan

チーム5

チーム名:ゲームウィキ麻生支部
リーダー:宮崎
サービス名:ゲームウィキ
プロジェクト名:GameWiki

9月11日

Gitの練習

todo.jsp を変更してみる。

<body>
<h1>ToDo項目</h1>

<form method="post" action="input">
<input type="text" name="item">
<input type="submit" value="登録">
</form>

<h3>未完了</h3>
<div id="todo">
<c:if test="${list != null}">
  <form method="post" action="check">
    <c:forEach var="t" items="${list}">
      <c:if test="${t.done == null}">
        <div id="check${t.id}">
      	  <input type="checkbox" name="items" value="${t.id}" />${t.item} <span class="days">${t.days}日前</span>
        </div>
      </c:if>
    </c:forEach>
  </form>
</c:if>
</div>

変更して保存すると、プロジェクトエクスプローラ上のファイル名の左側に「>」が表示される。
これは、リポジトリの最新の状態から変更があったことを示す。

プロジェクトを右クリックして[チーム]-[コミット]を選択すると、todo.jsp がファイルリストに表示される。
todo.jspをダブルクリックすると変更箇所が確認できる。
変更内容が間違いなければ、コミットメッセージを入力してコミットを実行する。

未完了項目の経過日数表示

未完了項目の右側に登録してからの経過日数を表示するようにする。
経過日数はサーバー側で計算した方が実装が簡単そうなので、ToDoエンティティに経過日数を格納する days を追加する。
このデータはデータベースに保存する必要はないので @Transient アノテーションを追加しておく。

package jp.abc;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Transient;

@Entity
public class ToDo {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column
	private long id;

	@Column
	private String item;

	@Column
	private Date created;

	@Column
	private Date done;

	@Transient
	private int days;

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public String getItem() {
		return item;
	}

	public void setItem(String item) {
		this.item = item;
	}

	public Date getCreated() {
		return created;
	}

	public void setCreated(Date created) {
		this.created = created;
	}

	public Date getDone() {
		return done;
	}

	public void setDone(Date done) {
		this.done = done;
	}

	public int getDays() {
		return days;
	}

	public void setDays(int days) {
		this.days = days;
	}
}

コントローラを見ると、あちこちで dao.getAll() を使っているので、コントローラではなくDAO実装内で追加すると一か所の変更で済ませることができる。

	public List<ToDo> getAll() {
		EntityManager manager = factory.createEntityManager();
		List<ToDo> list = null;
		CriteriaBuilder builder = manager.getCriteriaBuilder();
		CriteriaQuery<ToDo> q = builder.createQuery(ToDo.class);
		Root<ToDo> r = q.from(ToDo.class);
		q.select(r);
		list = (List<ToDo>)manager.createQuery(q).getResultList();
		manager.close();
		Date now = new Date();
		for (ToDo t : list) {
			long diff = now.getTime() - t.getCreated().getTime();
			diff = diff / (1000 * 60 * 60 * 24);
			t.setDays((int)diff);
		}
		return list;
	}

todo.jsp の未完了項目の右側に値を追加してみる。

<h3>未完了</h3>
<div id="todo">
<c:if test="${list != null}">
  <form method="post" action="check">
    <c:forEach var="t" items="${list}">
      <c:if test="${t.done == null}">
        <div id="check${t.id}">
      	  <input type="checkbox" name="items" value="${t.id}" />${t.item} ${t.days}日前
        </div>
      </c:if>
    </c:forEach>
  </form>
</c:if>
</div>

「○日前」の文字サイズや色を変更するために span タグで囲む。

      	  <input type="checkbox" name="items" value="${t.id}" />${t.item} <span class="days">${t.days}日前</span>

days クラス用のCSSを用意する。

<title>ToDoアプリ</title>
<style type="text/css">
span.days {
  font-size: 9pt;
  color: gray;
  margin-left: 20px;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

動作確認したら、プロジェクトを右クリックして[チーム]-[コミット]を選択する。
ToDo.java
ToDoDaoImpl.java
todo.jsp
の3ファイルがリストされる。
それぞれのファイルをダブルクリックして変更箇所を確認し、問題がなければコミットメッセージを入力してコミットを実行する。

完了済みの項目の経過日数

完了済みの項目の経過日数は、完了した日からの経過日数を表示する。

まずは、未完了項目と同じように、登録日からの経過日数を表示してみる。

<h3>完了済み</h3>
<div id="done">
<c:if test="${list != null}">
  <c:forEach var="t" items="${list}">
    <c:if test="${t.done != null}">
      <div id="check${t.id}">
        <input type="checkbox" name="items" value="${t.id}" checked="checked" />${t.item} <span class="days">${t.days}日前</span>
      </div>
    </c:if>
  </c:forEach>
</c:if>
</div>

完了項目に対しての days の値は、完了日からの経過日数となるように、サーバー側の ToDoDaoImpl.java で計算する。

	public List<ToDo> getAll() {
		EntityManager manager = factory.createEntityManager();
		List<ToDo> list = null;
		CriteriaBuilder builder = manager.getCriteriaBuilder();
		CriteriaQuery<ToDo> q = builder.createQuery(ToDo.class);
		Root<ToDo> r = q.from(ToDo.class);
		q.select(r);
		list = (List<ToDo>)manager.createQuery(q).getResultList();
		manager.close();
		Date now = new Date();
		for (ToDo t : list) {
			long from;
			if (t.getDone() == null) {
				from = t.getCreated().getTime();
			} else {
				from = t.getDone().getTime();
			}
			long diff = now.getTime() - from;
			diff = diff / (1000 * 60 * 60 * 24);
			t.setDays((int)diff);
		}
		return list;
	}

新しく項目を追加したり、チェックボックスをチェックすると「○日前」の表示が消えてしまう。
これは、上記操作をしたときに、jQueryでHTML要素を生成しなおしているため。
span要素を追加する処理を、jQueryで記述することで解決する。

function init() {
	$('input[name="items"]').change(function(){
		console.log($(this).prop('checked'));
		var url;
		if ($(this).prop('checked')) {
			url = 'done';
		} else {
			url = 'redo';
		}
		$.post(
			url,
			'id=' + $(this).val(),
			function(data) {
				console.log(data);
				$('#todo form').children().remove();
				$('#done').children().remove();
				for (var i = 0; i < data.length; i++) {
					var div = $('<div>');
					div.attr('id', 'check' + data[i].id);
					var cb = $('<input>')
								.attr('type', 'checkbox')
								.attr('name', 'items')
								.attr('value', data[i].id);
					div.append(cb).append(data[i].item)
					var s = $('<span>');
					s.addClass('days');
					s.text(data[i].days + '日前');
					div.append(s);
					if (data[i].done == null) {
						$('#todo form').append(div);
					} else {
						cb.attr('checked', 'checked');
						$('#done').append(div);
					}
				}
				init();
			});
	});
}

動作確認できたら、[チーム]-[コミット]で変更内容をGitリポジトリに反映させる。

これまでの変更内容は、[チーム]-[ヒストリーに表示]で確認できる。

9月8日

サーバーから受信したデータをもとに画面を更新するようにしたところ、最初のチェックに対してはToDo項目が完了済みに移動したが、2個目からは移動しなくなってしまった。

これは、change()イベントを拾う処理が画面読み込み完了時(load)のみに行われているため。

新しく生成した input[name=”items”] のセレクタで指定した要素にはイベントを拾う処理が登録されていないために動作しない。

この対策を実施する。
まずはload時の処理をfunctionに分割する。

<!DOCTYPE html>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>ToDoアプリ</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
	init();
})

function init() {
	$('input[name="items"]').change(function(){
		console.log($(this).parent());
		$('#done').append($(this).parent());
		$.post(
			'done',
			'id=' + $(this).val(),
			function(data) {
				console.log(data);
				$('#todo form').children().remove();
				$('#done').children().remove();
				for (var i = 0; i < data.length; i++) {
					var div = $('<div>');
					div.attr('id', 'check' + data[i].id);
					var cb = $('<input>')
								.attr('type', 'checkbox')
								.attr('name', 'items')
								.attr('value', data[i].id);
					div.append(cb).append(data[i].item);
					if (data[i].done == null) {
						$('#todo form').append(div);
					} else {
						cb.attr('checked', 'checked');
						$('#done').append(div);
					}
				}
			});
	});
}
</script>
</head>
<body>
<h1>ToDo項目</h1>

<form method="post" action="input">
<input type="text" name="item">
<input type="submit" value="登録">
</form>

<div id="todo">
<c:if test="${list != null}">
  <form method="post" action="check">
    <c:forEach var="t" items="${list}">
      <c:if test="${t.done == null}">
        <div id="check${t.id}">
      	  <input type="checkbox" name="items" value="${t.id}" />${t.item}
        </div>
      </c:if>
    </c:forEach>
  </form>
</c:if>
</div>

<hr />
<h3>完了済み</h3>
<div id="done">
<c:if test="${list != null}">
  <c:forEach var="t" items="${list}">
    <c:if test="${t.done != null}">
      <div id="check${t.id}">
        <input type="checkbox" name="items" value="${t.id}" checked="checked" />${t.item}
      </div>
    </c:if>
  </c:forEach>
</c:if>
</div>



</body>
</html>

この変更ではいままでどおりの動作となる。

さらに、サーバーからデータを受信して、新規に要素を生成して画面上に追加した処理のあとに init() を呼ぶようにすることで、チェックボックスのイベントに対しての処理を行うようにする。

<!DOCTYPE html>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>ToDoアプリ</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
	init();
})

function init() {
	$('input[name="items"]').change(function(){
		console.log($(this).parent());
		$('#done').append($(this).parent());
		$.post(
			'done',
			'id=' + $(this).val(),
			function(data) {
				console.log(data);
				$('#todo form').children().remove();
				$('#done').children().remove();
				for (var i = 0; i < data.length; i++) {
					var div = $('<div>');
					div.attr('id', 'check' + data[i].id);
					var cb = $('<input>')
								.attr('type', 'checkbox')
								.attr('name', 'items')
								.attr('value', data[i].id);
					div.append(cb).append(data[i].item);
					if (data[i].done == null) {
						$('#todo form').append(div);
					} else {
						cb.attr('checked', 'checked');
						$('#done').append(div);
					}
				}
				init();
			});
	});
}
</script>
</head>
<body>
<h1>ToDo項目</h1>

<form method="post" action="input">
<input type="text" name="item">
<input type="submit" value="登録">
</form>

<div id="todo">
<c:if test="${list != null}">
  <form method="post" action="check">
    <c:forEach var="t" items="${list}">
      <c:if test="${t.done == null}">
        <div id="check${t.id}">
      	  <input type="checkbox" name="items" value="${t.id}" />${t.item}
        </div>
      </c:if>
    </c:forEach>
  </form>
</c:if>
</div>

<hr />
<h3>完了済み</h3>
<div id="done">
<c:if test="${list != null}">
  <c:forEach var="t" items="${list}">
    <c:if test="${t.done != null}">
      <div id="check${t.id}">
        <input type="checkbox" name="items" value="${t.id}" checked="checked" />${t.item}
      </div>
    </c:if>
  </c:forEach>
</c:if>
</div>



</body>
</html>

チェックボックスのチェックを外された時は、ToDo項目を完了済みから未完了に移動させるようにする。

サーバー側では、URL /redo を用意する。処理の内容は、done()とほぼ同じで、ToDoエンティティのcreatedの値を現在時刻にして、doneの値をnullに戻すだけの違い。

 	@RequestMapping(value = "/redo", method = RequestMethod.POST)
	@ResponseBody
	@ResponseStatus(value = HttpStatus.OK)
	public List<ToDo> redo(@RequestParam(value = "id") String id, Model model) {
		ToDoDao<ToDo> dao = new ToDoDaoImpl();
		ToDo todo = dao.findById(Long.parseLong(id));
		todo.setCreated(new Date());
		todo.setDone(null);
		dao.update(todo);
		List<ToDo> list = dao.getAll();
		return list;
	}

ブラウザ側では、完了済みにあるチェックボックスのチェックが外されたときは、redo に POST 送信するように、JavaScriptコードを書き換えればよい。

<!DOCTYPE html>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>ToDoアプリ</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
	init();
})

function init() {
	$('input[name="items"]').change(function(){
		console.log($(this).prop('checked'));
		var url;
		if ($(this).prop('checked')) {
			url = 'done';
		} else {
			url = 'redo';
		}
		$.post(
			url,
			'id=' + $(this).val(),
			function(data) {
				console.log(data);
				$('#todo form').children().remove();
				$('#done').children().remove();
				for (var i = 0; i < data.length; i++) {
					var div = $('<div>');
					div.attr('id', 'check' + data[i].id);
					var cb = $('<input>')
								.attr('type', 'checkbox')
								.attr('name', 'items')
								.attr('value', data[i].id);
					div.append(cb).append(data[i].item);
					if (data[i].done == null) {
						$('#todo form').append(div);
					} else {
						cb.attr('checked', 'checked');
						$('#done').append(div);
					}
				}
				init();
			});
	});
}
</script>
</head>
<body>
<h1>ToDo項目</h1>

<form method="post" action="input">
<input type="text" name="item">
<input type="submit" value="登録">
</form>

<div id="todo">
<c:if test="${list != null}">
  <form method="post" action="check">
    <c:forEach var="t" items="${list}">
      <c:if test="${t.done == null}">
        <div id="check${t.id}">
      	  <input type="checkbox" name="items" value="${t.id}" />${t.item}
        </div>
      </c:if>
    </c:forEach>
  </form>
</c:if>
</div>

<hr />
<h3>完了済み</h3>
<div id="done">
<c:if test="${list != null}">
  <c:forEach var="t" items="${list}">
    <c:if test="${t.done != null}">
      <div id="check${t.id}">
        <input type="checkbox" name="items" value="${t.id}" checked="checked" />${t.item}
      </div>
    </c:if>
  </c:forEach>
</c:if>
</div>



</body>
</html>

Gitによるバージョン管理

Eclipseでプロジェクトを右クリックし、[チーム]-[プロジェクトの共用]を選択する。
リポジトリタイプは「Git」を選択し「次へ」をクリックする。
リポジトリの「作成」をクリックし、リポジトリディレクトリに「C:\Users\name\git」が指定されている。
「完了」ボタンが押せない場合は、フォルダ名を適当に変更して「完了」ボタンが有効になるようにし、「完了」をクリックする。
「完了」すると、プロジェクト名の右側に「NO-HEAD」の表示が出る。

プロジェクトを右クリックし、[チーム]-[コミット]を選択すると、画面の下にコミット対象となるファイルの一覧が表示される。余計なファイルがたくさん含まれているので、これを除外する設定を行う。
リポジトリ内にあるプロジェクトのフォルダ内に「.gitignore」ファイルを作成する。
エクスプローラでは新規ファイルでテキストファイル「gitignore.txt」を作成する。
コマンドプロンプトを起動してファイル名を変更する。

> ren gitignore.txt .gitignore

.gitignore ファイル内に、除外するファイルやフォルダ名を記載する。

/target
.settings/
.classpath
.project
/pleiades45

.gitignore ファイルを保存したら、Eclipseでプロジェクトを右クリックし「リフレッシュ」を選択する。
プロジェクトを右クリックし[チーム]-[コミット]を選択する。
コミット対象のファイル一覧に余計なファイルが含まれていないことを確認したら、コミットメッセージに変更理由(今回は最初なので「新規作成」でもよい)などを記載して「コミット」をクリックする。

コミットが成功したら、プロジェクト名の右側が「master」に変化する。

9月4日

前回は、ToDo項目にチェックすると、AJAXでサーバーにデータを送信して、完了日時をエンティティに登録し、データベースを更新するところまで実装した。

この状態でToDo項目の画面にアクセスすると、完了済みの項目が未完了に表示されてしまう。

未完了のToDo項目と、完了済みのToDo項目をそれぞれの領域に表示するよう修正する。

<!DOCTYPE html>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>ToDoアプリ</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
	$('input[name="item"]').change(function(){
		console.log($(this).parent());
		$('#done').append($(this).parent());
		$.post('done', 'id=' + $(this).val());
	});
})
</script>
</head>
<body>
<h1>ToDo項目</h1>

<form method="post" action="input">
<input type="text" name="item">
<input type="submit" value="登録">
</form>

<c:if test="${list != null}">
  <form method="post" action="check">
    <c:forEach var="t" items="${list}">
      <c:if test="${t.done == null}">
        <div id="check${t.id}">
      	  <input type="checkbox" name="item" value="${t.id}" />${t.item}
        </div>
      </c:if>
    </c:forEach>
  </form>
</c:if>

<hr />
<h3>完了済み</h3>
<div id="done">
<c:if test="${list != null}">
  <c:forEach var="t" items="${list}">
    <c:if test="${t.done != null}">
      <div id="check${t.id}">
        <input type="checkbox" name="item" value="${t.id}" checked="checked" />${t.item}
      </div>
    </c:if>
  </c:forEach>
</c:if>
</div>



</body>
</html>

テキストフィールドに入力して「登録」ボタンを押すと、テキストフィールドが完了済みに移動してしまう問題を解決する。

jQueryで input[name=”item”] というセレクタを指定していると、これがテキストフィールドにも該当してしまう。
チェックボックスのnameをitemsに変更してchangeイベントを拾うところもinput[name=”items”]とする。

<!DOCTYPE html>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>ToDoアプリ</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
	$('input[name="items"]').change(function(){
		console.log($(this).parent());
		$('#done').append($(this).parent());
		$.post('done', 'id=' + $(this).val());
	});
})
</script>
</head>
<body>
<h1>ToDo項目</h1>

<form method="post" action="input">
<input type="text" name="item">
<input type="submit" value="登録">
</form>

<c:if test="${list != null}">
  <form method="post" action="check">
    <c:forEach var="t" items="${list}">
      <c:if test="${t.done == null}">
        <div id="check${t.id}">
      	  <input type="checkbox" name="items" value="${t.id}" />${t.item}
        </div>
      </c:if>
    </c:forEach>
  </form>
</c:if>

<hr />
<h3>完了済み</h3>
<div id="done">
<c:if test="${list != null}">
  <c:forEach var="t" items="${list}">
    <c:if test="${t.done != null}">
      <div id="check${t.id}">
        <input type="checkbox" name="items" value="${t.id}" checked="checked" />${t.item}
      </div>
    </c:if>
  </c:forEach>
</c:if>
</div>



</body>
</html>

pom.xmlにJSONを扱うためのライブラリを追加する。

		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-core</artifactId>
			<version>2.8.8</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.8.9</version>
		</dependency>

追加したら、[maven]-[プロジェクトの更新]と[実行]-[maven install]を実行する。

ToDo項目を更新するための /done に対してPOST送信したら、その応答で、JSONデータを返すようにする。

	@RequestMapping(value = "/done", method = RequestMethod.POST)
	@ResponseBody
	@ResponseStatus(value = HttpStatus.OK)
	public List<ToDo> done(@RequestParam(value = "id") String id, Model model) {
		ToDoDao<ToDo> dao = new ToDoDaoImpl();
		ToDo todo = dao.findById(Long.parseLong(id));
		todo.setDone(new Date());
		dao.update(todo);
		List<ToDo> list = dao.getAll();
		return list;
	}

jQueryでPOSTしたときに応答を受信してJavaScriptコンソールにデータを出力してみる。

<script type="text/javascript">
$(function(){
	$('input[name="items"]').change(function(){
		console.log($(this).parent());
		$('#done').append($(this).parent());
		$.post(
			'done',
			'id=' + $(this).val(),
			function(data) {
				console.log(data);
			});
	});
})
</script>

チェックして、サーバーからデータを受け取ったら、受け取ったデータに基づいてToDo項目を更新する。
まずは、既存の項目をすべて削除する。

<script type="text/javascript">
$(function(){
	$('input[name="items"]').change(function(){
		console.log($(this).parent());
		$('#done').append($(this).parent());
		$.post(
			'done',
			'id=' + $(this).val(),
			function(data) {
				console.log(data);
				$('#todo form').children().remove();
				$('#done').children().remove();
			});
	});
})
</script>

受信したJSONデータにもとづいてdiv要素を作り直す。
完了日時が設定されていなければ未完了側に、設定されていれば完了側にdiv要素を追加する。

$(function(){
	$('input[name="items"]').change(function(){
		console.log($(this).parent());
		$('#done').append($(this).parent());
		$.post(
			'done',
			'id=' + $(this).val(),
			function(data) {
				console.log(data);
				$('#todo form').children().remove();
				$('#done').children().remove();
				for (var i = 0; i < data.length; i++) {
					var div = $('<div>');
					div.attr('id', 'check' + data[i].id);
					var cb = $('<input>')
								.attr('type', 'checkbox')
								.attr('name', 'items')
								.attr('value', data[i].id);
					div.append(cb).append(data[i].item);
					if (data[i].done == null) {
						$('#todo form').append(div);
					} else {
						cb.attr('checked', 'checked');
						$('#done').append(div);
					}
				}
			});
	});
})
</script>