Quantcast
Channel: Eclipse RCP, RAP Blog
Viewing all 71 articles
Browse latest View live

(5)Spring Boot で Web アプリケーションを開発:Thymeleaf と連携する

$
0
0
このシリーズの(1)、(2)で紹介したように、Spring Boot Web アプリの雛形プロジェクトを作成した後は、Spring MVC で開発する場合とあまり変わりません。Spring の基本設定もデフォルト状態で使う場合、Spring Boot がやってくれるので簡単です。今回はSpring Boot + Thymeleaf でよく使う機能をおさらいしてみました。
  1. テンプレートフラグメントのインクルード
  2. Thymeleaf の日付、数値ユーティリティの使い方
  3. リンクURL式 @{...} を使って画像ファイルを指定する
  4. リクエストパラメータを表示する
  5. セッション属性を表示する
  6. 変数式 ${...}、繰り返し(th:eachと繰り返し変数)、条件判定
  7. 選択変数式 *{...} の使い方
  8. 変数式 ${...} を javascript の変数に代入する

プロジェクトの作成

(1)Spring Boot で Web アプリケーションを開発:プロジェクトの作成」と同じように、新規プロジェクト(プロジェクト名は bootThyme) を作成します。
Artifact id = bootThyme
Name = bootThyme

最終的なプロジェクトの構造は以下のようになります:
大部分のソースは SpBootDemo と変わりませんが、異なる部分を以下に掲載します。

モデルを追加

このサンプル・アプリで使用するモデルクラスを作成します。Tutorial: Using Thymeleaf (ja)で使用されているモデルと同じものを作成します。

home.html の作成

サンプル・アプリ起動時に表示されるビュー src/main/resources/templates/views/home.html を作成します。
上の home.html で Thymeleafを使っている部分を順に見ていきます。
  • 4行: th:include="fragment/frag01 :: htmlhead"
    これは複数のページで共通のコード断片(上の場合 src/main/resources/templates/views/fragment/frag01.html で定義した htmlhead フラグメント)をインクルードします。
  • 5行: th:with="title=#{app.title}, navColorCss='navBarBlue01'"
    共通部分の一部をパラメータで置き換えることができます。ここでは
    • titleパラメータでヘッダのタイトルに #{app.title} を設定します。さらに #{app.title} は messages.properties で app.title プロパティにセットされた文字列 "Spring Boot + Thymeleaf"に置き換えられます。
    • navColorCssパラメータでナビゲーションバーのスタイルを設定する CSS ファイルを指定していします。
    この設定により home ビューにブルーのナビゲーションバーが表示され、タイトルとして "Spring Boot + Thymeleaf"が表示されます。
  • 8-9行:th:include="fragment/frag01 :: navbar" th:with="title=#{app.title}"
    4-5行と同じですが、ここではインクルードするフラグメントとして navbar(ナビゲーションバー)を指定します。title パラメータには5行と同じく #{app.title}を設定しています。
  • 41-42行:"fragment/frag01 :: footer" th:with="title=#{app.footer}"
    これも 4-5行と同じで、ページのフッター部分(footerフラグメント)をインクルードします。
  • 16行:th:utext="#{home.welcome(${userName})}
    この行では、home ページのウェルカムメッセージを表示しています。 <h3>タグや<span>タグなどでテキストを出力するには th:text, th:utext を使用します。ここでは、<h3>のテキストとして messages.properties の home.welcome プロパティの内容 "{0} さん、これは <b>Spring Boot Demo アプリケーション!</b> です。"を出力します。さらに、{0}の部分は Thymeleaf の変数式 ${userName} で置き換えられます。<b>によるボールド指定を有効にするには th:utext を使用する必要があります(th:text では自動的にエスケープされます)。${userName} の値は HomeController.home() の Model#addAttribute("userName", "ログインユーザー"); によりセットされた "ログインユーザー"です。
  • 19行:th:text="${today}
    addAttribute("today", Calendar.getInstance().getTime()); によりセットされたシステム日を表示します。
  • 24-38行:home.html から Thymeleaf の使い方を示すサンプルページを呼び出します。

home ビューの画面は次のようになります。


Thymeleaf の使い方サンプル

・Thymeleaf の日付、数値ユーティリティの使い方

  • 10行:th:text="${#dates.format(today,'yyyy/MM/dd EE')}"
    17行:th:text="${#dates.format(today,'yyyy/MMM/dd HH:mm')}"コントローラから送られたシステム日(today) に 書式'yyyy/MM/dd EE' ,'yyyy/MMM/dd HH:mm'を設定します。
  • 23行:th:text="${#dates.createToday()}"
    現在日のオブジェクトを作成します。
  • 29行:th:text="${#dates.year(today)}"
    渡された日付引数から日付プロパティを取得します。この例はでは"年"を取得します。 "月"や "日"を取得するには、#dates.month(..)、#dates.day(..) を使用します。
  • 39行:th:text="${#numbers.formatDecimal(double1,5,3)}"
    コントローラから送られた double 値に "00000.000"の書式を設定します。 1234.567 は 01234.567 で出力されます。ゼロパディングをしないようにするには(double1,1,3)のように指定します。コントローラから送られた値が int 値や String 値の場合でも同じように使用できます。

sample01 ビューを表示して上記の結果を確認します:


・リンクURL式 @{...} を使って画像ファイルを指定する

  • 6行:th:src="@{./img/Red Apple.gif}"
    7行:th:src="@{img/Sunflower.gif}"
    前者は "src/main/resources/static/img/Red Apple.gif"を、後者は "src/main/resources/static/img/Sunflower.gif"を参照します。
    その他の例: th:href="@{/css/app_base.css}"は、 "src/main/resources/static/css/app_base.css"を参照します。

sample02 ビューの表示結果:


・リクエストパラメータを表示する

  • 21行:th:text="${param.size()}"
    リクエストパラメータのサイズを表示します。このページは "@{sample03?param1=aaa}"で呼ばれるので、サイズは1となります。
  • 22行:th:text="${param.isEmpty()}"
    リクエストパラメータの有無を判定します。パラメータは有るので、false を表示します。
  • 23行:th:text="${param.param1 + ''}"
    パラメータ param1 の内容を表示します。 aaa が表示されます。
sample03 ビューの表示結果(1):

  • 2-3行:th:unless="${param.param2}" th:include="fragment/frag01 :: htmlhead"
    4行:th:with="title=#{app.title}"
    5-6行:th:if="${param.param2}" th:include="fragment/frag01 :: htmlhead"
    7行:th:with="title=#{app.title}, navColorCss='navBarRed01'"
    あまり実用的な例ではありませんが、リクエストパラメータ param2 の有無を判定してナビゲーションの色を変えます。param2 が無ければ(th:unless="${param.param2}")、2-4行が有効になって、パラメータ navColorCss は設定されません。param2 が有れば(th:if="${param.param2}")、5-7行が有効になって、パラメータ navColorCss に 'navBarRed01'が設定されます。ブラウザのURL入力で、"localhost:9000/bootThyme/sample03?param1=aaa&param2=1"を入力してください。赤いナビゲーションバーが表示されます。

sample03 ビューの表示結果(2):


・セッション属性を表示する

  • 12行:th:text="${session.size()}"
    HttpSession に登録されたオブジェクトのサイズを表示します。HttpController.sample4() では
        session.setAttribute("str1", "文字列");
    session.setAttribute("cust2", new Customer(102, "顧客00002",
    DateTime.now().minusMonths(20).toCalendar(Locale.JAPAN)));
    で2つのオブジェクトを登録しているのでサイズはになります。
  • 13行:th:text="${session.isEmpty()}"
    セッション属性の有無を判定します。2つのオブジェクトが有るので falseが表示されます。
  • 14行:th:text="${session.str1 + ''}"
    "str1"の内容 "文字列"が表示されます。
  • 15行:th:text="${session.cust2.name + ''}"
    "cust2.name"の内容 "顧客00002"が表示されます。

sample04 ビューの表示結果:


・変数式 ${...}、繰り返し(th:eachと繰り返し変数)、条件判定

  • 15行:th:each="prod : ${products}"
    ${products} はHomeController.sample05() でモデルに登録した List<Product> を参照します。th:each はリストの要素数だけ繰り返します。各要素は、繰り返し変数 prod で参照できます。<tr> タグに記述した場合、テーブル行が要素の数だけ出力されます。
  • 16行:th:class="${prodStat.odd} ? 'odd'"
    prodStat ステータス変数は Thymeleaf により自動的に作成され、繰り返しの状態を保持します(prodStat という名前は prod にStat が自動的に追加されたものです)。
    ステータス変数は属性の中で定義され、以下の内容を保持しています:
    • index: 0始まりの現在の「繰り返しインデックス」
    • count: 1始まりの現在の「繰り返しインデックス」
    • size: 被繰り返し変数の全要素数
    • current: 繰り返し中の「繰り返し変数」
    • even/odd: 現在の繰り返し処理が偶数か奇数か
    • first: 現在の繰り返し処理が最初かどうか
    • last: 現在の繰り返し処理が最後かどうか
    prodStat.odd は奇数行を表すので、この行は行が奇数行目なら class に odd を設定します。
  • 17行:th:text="${prod.id}"
    対象行のID列に、リスト内の該当 Product インスタンスの id の値を表示します。
  • 20行:th:text="${prod.inStock} ? 'o' : 'x'"
    prod.inStock が true なら 'o'を false なら 'x'を表示します。
  • 23-24行:th:if="${prod.inStock}==true" th:id="${prod.id}" onclick="alert(this.id)"
    th:if で ${prod.inStock} の true | false を判定しています。20行のように ${prod.inStock} としても同じです。判定の結果が true の場合は23-24行が有効になり、出荷ボタンが表示されます。
  • 33行:th:if="!${#lists.isEmpty(prod.comments)}"
    #lists ユーティリティを使って製品のコメント(prod.comments)の有無を判定します。コメントがあれば33-37行が有効になり、コメントの件数を表示するボタンとコメント一覧を表示します。
  • 35行:th:each="cm : ${prod.comments}"
    Product の comments の数だけ繰り返して、コメントをドロップダウンとして追加します。

sample05.html ビューの表示結果:


・選択変数式 *{...} の使い方

  • 4行:th:object="${customer}"Customer オブジェクトを選択します。この <div> の範囲内で、選択式 *{..} を使って選択オブジェクトのプロパティを参照できます。HomeController.sample06() の
    addAttribute("customer", new Customer(101, "顧客00001",
    DateTime.now().minusMonths(13).toCalendar(Locale.JAPAN)));
    で登録した Customer インスタンスを参照します。
  • 8行:th:text="*{id}"
    ${customer.id} と全く同じです。101を表示します。
  • 11行:th:text="*{#calendars.format(customerSince,'yyyy/MM/dd')}"
    *{customerSince} に #calenndars ユーティリティを使ってでフォーマットを設定する例です。
  • 17行:th:text="'(' + *{id} + ')'"
    18行:th:text="*{name + 'さん'}"
    *{..} にテキストを追加する例です。前者の結果は "(101)"、後者の結果は"顧客00001さん"となります。 
  • 20行:th:utext="*{customerSince} < ${#calendars.create(2015,1,1)} ? '長期' : '短期'"
    *{customerSince} と日付ユーティリティで生成した日付オブジェクトを比較して'長期'または'短期'を表示します。

sample06 ビューを表示します:


・変数式 ${...} を javascript の変数に代入する

  • 20行:th:inline="javascript"
    21行:var cust = [[${customer}]];
    cust 変数に、HomeController.sample07() でモデルに追加した Customer インスタンスを代入します。
  • 29行:JavaScript で、代入された Custom インスタンスを使用する例です。

sample07 ビューを表示します:

ソースコード:GitHub の bootThymeからダウンロードできます。

(6)Spring Boot で Web アプリケーションを開発:セキュリティ

$
0
0
今回は、Spring Boot Web アプリケーションでセキュリティ機能をサポートします。

プロジェクトの作成

前回「(5)Thymeleaf と連携する」と同様に、「(1)プロジェクトの作成」と同じ方法で新規プロジェクト bootSec を作成します。
ほとんどの部分は前回と同様です。異なる部分は以下の通りです:

POMの修正

セキュリティを有効にするために、POMに以下の部分を追加します。

SecConfig.java の追加

セキュリティ設定クラスを作成します:

  • 6行:antMatchers("/", "/home").permitAll()
    これにより、"/"や "/home"リクエストにはログインは要求されません。それ以外のページへのアクセスはログインが必要になります。
  • 7行:antMatchers("/admin/**").hasRole("ADMIN")
    これにより、"/admin/**"リクエストに対して "ROLE_ADMIN"ロールのユーザーのみが承認されます。
  • 9行:loginPage("/login").defaultSuccessUrl("/userHello").permitAll()
    ログイン成功時は userHello ページを表示します。
  • 17行:withUser("tosi").password("tosi").roles("USER")ユーザー名/パスワードが tosi/tosi のユーザーを "ROLE_USER"ロールとして設定します。
  • 17行:withUser("yumi").password("yumi").roles("ADMIN")
    ユーザー名/パスワードが yumi/yumi のユーザーを "ROLE_ADMIN"ロールとして設定します。

ビューの作成

このアプリケーションで使用するビューを作成します:
上記のビューで共通にインクルードされるナビゲーションのフラグメントは以下の通りです:

アプリケーションの動作確認

1.アプリケーションを起動する

アプリケーションを起動して、ブラウザで "localhost:9001/bootSec"にアクセスします。
最初に home ビューが表示されます:


このビューは SecConfig#configure()で antMatchers("/", "/home").permitAll()と設定されているのでログインは要求されません。

2.ユーザーページを表示する

上の home ページで「ユーザーページ」を見るをクリックします。ユーザーページを見るにはログインが必要です:


ユーザー名/パスワードに tosi/tosi を入力して"リターン"キーを押すと、ユーザーページが表示されます:


3.管理者ページを表示する

上のユーザーページでナビゲーションバーの"String Boot + セキュリティ"の部分をクリックすると home ビューが表示されます。今度は「管理者ページ」を見るをクリックします。 管理者ページは SecConfig#configure()で antMatchers("/admin/**").hasRole("ADMIN")と設定されているので、USER ロールでは表示できません。その結果、次に示すエラー画面が表示されます:


ナビゲーションバーの "ログアウト"ボタンを押してログアウトすると、ログイン画面が表示されます。

今度は、ユーザー名/パスワードに yumi/yumi を入力して"リターン"キーを押すと、ユーザーページが表示されます:


ナビゲーションバーには、ログインユーザー情報として"yumi (管理者)"が表示されます。
home に戻って、「管理者ページ」を見るをクリックすると、今度は管理者ページが表示されます:


ソースコードは GitHub bootSecからダウンロードできます。

(7)Spring Boot で Web アプリケーションを開発:入力の検証

$
0
0
今回は、フォーム入力の検証を行うサンプル・アプリケーションを作成します。

プロジェクトの作成

前回同様に、「(1)プロジェクトの作成」と同じ方法で新規プロジェクト bootValidate を作成します。
ほとんどの部分は前回と同様です。異なる部分は以下の通りです:


コントローラの作成

bootvalid.controller.FormController クラスを作成します:

  • 13-18行:
    アプリ起動時の "/", "/productForm" GET リクエストに対して、製品入力ビュー "views/valid/form"を表示します。
  • 26-35行:
    製品入力ビューで、「保存」ボタンを押すと productValid() メソッドが呼ばれ、入力情報を検証(Bean Validation)します。
    29行:@Valid Product product
    @Valid注釈により入力情報 product に対して検証を行います。
    30行:BindingResult bindingResult
    検証結果は、bindingResult に格納されます。
    31-33行:
    bindingResult.hasErrors()でエラーがあるか調べて、エラーがあれば製品入力ビューに戻ります。エラーがなければ結果ビュー "/results"を表示します。
登録済みのデータと比較したり、製品名のユニーク判定などの複雑なデータ検証が必要な場合は、当然ですが自前で実装します。


モデルの作成

@Valid注釈により入力情報を検証すると書きましたが、個別の項目(製品名、製品価格など)に対しては Bean Validation の標準注釈を付けることで、検証ルールを適用できます。検証ルールを設定した Product クラスを作成します:
  • 7-8行:@NotNull:必須入力を設定。
    message を明示的に指定しない場合はデフォルトのメッセージ(・・・)が表示されます。
  • 11-14行:@Size(min=3, max=30, message="製品名は、{min}から{max}文字の範囲で入力してください")
    入力文字数を 3-30文字に制限します。エラーの場合、明示的に指定したメッセージが表示されます。
  • 17-22行:@Min(value=300, message="製品価格は、{value}以上の値を入力してください")
    最小値を300に設定します。エラーの場合、明示的に指定したメッセージが表示されます。

ビューの作成

製品入力ビュー(views/valid/form.html):


  • 7-9行:th:action="@{/productForm}" th:object="${product}"
    id が "productForm"のフォームを作成します。th:object により、コマンドオブジェクト(フォーム・バッキング・ビーン)として、Productオブジェクトを指定します。th:action は、フォーム送信時のアクションとして "/productForm"リクエストを指定します。これにより、「保存」ボタンが押されたら FormController.productValid() が呼ばれます。入力情報は、引数の product で渡されます。
  • 12行:th:attrappend="class=${#fields.hasErrors('id')} ? ' has-error'"
    検証エラーがある場合は、class 属性に has-error を追加します。
  • 15-17行:<input type="text" th:field="*{id}" ...
    フォームに製品ID の入力フィールド(input)を追加します。th:field は、選択オブジェクト ${product} の id を参照します。
  • 18行:th:if="${#fields.hasErrors('id')}"
    'id'で参照されるフィールドに検証エラーがあればエラー・メッセージを表示します。

アプリケーションの動作確認

1.アプリケーションを起動する:画面1

アプリケーションを起動して、ブラウザで "localhost:9002/bootValid"にアクセスします。
最初に製品入力ビュー views/valid/form が表示されます:


2.バリデーション・エラー(1):画面2

画面1で、何も入力しないで「保存」ボタンを押します。
  • 製品ID: product.id の検証ルール @NotNull に違反するので、Spring のデフォルトのエラー・メッセージ(messages.properties の NotNull=入力必須項目です)が表示されます。
  • 製品名: product.name の @Size(min=3, max=30, message="製品名は、{min}から{max}文字の範囲で入力してください")に違反するので、”製品名、3から30文字の範囲で入力してください"が表示されます。
  • 価格: product.price の @NotNull に違反するので、Spring のデフォルトのエラー・メッセージ(messages.properties の NotNull=入力必須項目です)が表示されます。

3.バリデーション・エラー(2):画面3

上の画面で、製品ID に "101"、製品名に "aaa"、価格に "200"を入力して「保存」ボタンを押します:

  • 価格: product.price の別の検証ルール @Min(value=300, message="製品価格は、{value}以上の値を入力してください") に違反するので、"製品価格は、300以上の値を入力してください"が表示されます。

4.エラーがない場合:画面4

上の画面で全ての項目に違反のない値を入力して、「保存」を押すと、結果ビューが表示されます:


5.バリデーション・エラー(Ajax版):画面5

上の画面でナビゲーションバーの "Spring Boot + バリデーション"の部分をクリックして画面1に戻ります。
画面1で、何も入力しないで「保存(Ajax版)」ボタンを押します。

「保存(Ajax版)」ボタンを押すと、上述の form.html/63-98行 の javascript 関数が実行されます。この関数では jQuery.ajax() により、入力された製品情報を url:"saveProduct"に送ります。これにより、FormController.saveProduct() が実行されます。
FormController の saveProduct() の内容を次に示します:

  • 10-12行:入力情報 product の検証を行います。
  • 14-20行:エラーがある場合はコマンドオブジェクトの StatusCmd の fldErrors にエラーメッセージ、 status にエラーコードを設定します。
    エラーコードが設定された場合、form.html/85-89行 の javascript コードが実行されて、エラーメッセージが表示されます。

2.エラーがない場合(Ajax版)

画面1または画面5で、検証ルールに違反しないデータを入力して「保存(Ajax版)」ボタンを押します。
処理が成功した時に、StatusCmd に設定されたメッセージ "製品:xxxxを追加しました"が表示されます:

※本当は、処理成功時は入力欄をクリアした方がいいと思いますが、何を入力したか確認するためこのサンプルではクリアしていません。



ソースコードは GitHub bootValidからダウンロードできます。

(8)Spring Boot で Web アプリケーションを開発:エクスポート処理(ダウンロード)

$
0
0
前回までで見たように、Spring Boot での開発は、Spring MVC で開発する場合とあまり変わりませんが、新しいフレームワークを使う場合、微妙な違いにハマる事もあります。今回はファイルのダウンロード機能を追加してそのあたりを調べてみました。

プロジェクトの作成

前回同様に、「(1)プロジェクトの作成」と同じ方法で新規プロジェクト bootExport を作成します。
ほとんどの部分は前回と同様です。異なる部分は以下の通りです:

Spring MVC との違い

1.依存ライブラリ/アプリケーション設定

前回のサンプル・プロジェクトのPOMと同じで、特に変更はありません。アプリの設定では、MVC プロジェクトの場合のようにビューリゾルバーを追加する必要はありません。

2.コントローラとビュー

Spring MVC で作成したコードをそのまま流用できますが、このサンプル・アプリでは、製品(Product)をダウンロードするように変更します。
bootExport.controller.ExportController:

  • 15-16行:model.addAttribute("products", createProducts(TEST_DATA_COUNT));
    ダミーの製品データ50件をモデルに追加して、製品一覧ビュー "views/productList"を表示します。
  • 24-38行:
    "/exportExcel"リクエストに応答して、excelファイルをエクスポートします:
    • 26行:ModelAndView mav = new ModelAndView(new ExcelBuilder());
      ドキュメントビューとして ExcelBuilder を設定します。
    • 27-28行:エクスポートする Excelファイル名を設定します。
    • 29行:mav.addObject("products", createProducts(TEST_DATA_COUNT));
      ダミーの製品データ50件をモデルに追加します。
  • 38-43行:
    "/exportPdf"リクエストに応答して、PDF レポートを表示します:
    • 40行:ModelAndView mav = new ModelAndView(new PdfBuilder());
      ドキュメントビューとして PdfBuilder を設定します。
上述の 15-16行で表示される製品一覧ビュー(views/productList.html)を作成します:

3.PDFのエクスポート

org.springframework.web.servlet.view.document.AbstractPdfView を拡張した PdfBuilder クラスを作成します:
上述した ExportController.exportPdf() から PdfBuilder.buildPdfDocument() が呼ばれます。
実際の処理は AbstractCrePdf を拡張した ProductCrePdf クラスの createPdf() メソッで行います。
bootexport.view.pdf.AbstractCrePdf と ProductCrePdf クラスを以下に示します:

  • 9行:this.products = (List) model.get("products");
    コントローラでセットした製品リスト "products"を取得します。
  • 16-23行:出力するテーブルのヘッダ、列幅、配置などを設定します。
  • 24-26行:抽象クラス AbstractCrePdf の createDefaultSettingTable() メソッドで PDFテーブルを作成して、ヘッダ行、枠線の設定などを行います。
  • 29-38行:製品リストの製品ごとにテーブル行(列:id, name, price, inStock)を出力します。

4.Excelのエクスポート

org.springframework.web.servlet.view.document.AbstractExcelView を拡張した ExcelBuilder クラスを作成します:
上述した ExportController.exportExcel() から ExcelBuilder.buildExcelDocument() が呼ばれます。
実際の処理は AbstractCreHSSFSheet を拡張した ProductCreHSSFSheet クラスの createSheet() メソッドが行います。
bootexport.view.excel.AbstractCreHSSFSheet と ProductCreHSSFSheet クラスを以下に示します:

  • 8行:this.products = (List) model.get("products");
    コントローラでセットした製品リストを取得します。
  • 14行:指定されたワークブックにシートを作成します。
  • 15-22行:シートのヘッダ行の設定を行います。
  • 24行:シートにヘッダ行を出力します。
  • 25-40行:製品リストの製品ごとに、シートにデータ行を出力します。

結果の確認

1.アプリケーションを起動:画面1

アプリケーションを実行して、ブラウザで "localhost:9003/bootExport"を入力します: 製品一覧ビュー views/productList が表示されます:


2.PDFを出力:画面2

上の画面でメニュー ".../..."をクリックして、製品一覧表(PDF)を出力します。


3.Excelファイルを出力:画面3

画面1でメニュー ".../..."をクリックして、製品一覧表(Excelファイル:"")を出力します。


まとめ

設定に関する違いはリファレンスなどで調べるしかありませんが、コントローラやビューについては Spring MVC で開発したコードが再利用できます。Spring Boot の利点を活かして Web アプリケーションやスタンドアロンのデスクトップ・アプリケーションを共通の UI(例えば HTML5 + Thymeleaf + Bootstrap)で簡単に作成できると思います。

ソースコードを GitHub : bootExportからダウンロードできます。

(9)Spring Boot で Web アプリケーションを開発:インポート処理(アップロード)

$
0
0
前回に続いて今回はファイルのアップロード機能を実装します。

プロジェクトの作成

前回同様に、「(1)プロジェクトの作成」と同じ方法で新規プロジェクト bootImport を作成します。
ほとんどの部分は前回と同様です。異なる部分は以下の通りです:

Spring MVC との違い

1.依存ライブラリ/アプリケーション設定

Spring MVC の雛形プロジェクトでは、POM で 依存ライブラリとして、Apache の commons-fileupload を追加しましたが、Spring Boot では必要ありません。
アプリケーション設定で、マルチパートリゾルバ(MultipartResolver)ビーンを追加しましたが、これも不要です。ただしアップロードファイルに対して最大ファイルサイズなどの指定を行う場合は、app.properties で次のように設定します。
multipart.maxFileSize: 128KB
multipart.maxRequestSize: 128KB

2.コントローラとビュー

ダウンロードの場合と同様に、Spring MVC で作成したコードをそのまま流用できますが、このデモアプリでは製品一覧にアップロードファイル(Excelファイル)をドラッグ・アンド・ドロップしてアップロード処理を行うようにしています。
インポートコントローラ bootimport.controller.ImportController は以下のようになります:

  • 35-39行:製品リスト "products"、スタータスコマンド "cmd"をモデルにセットして、製品一覧ビュー "views/productList"を表示します。
  • 48-53行:"/productList" Post リクエストに応答して、インポート処理を行います。
  • 114-158行:upload() メソッドはアップロードに共通の処理を行います。
    • 117-119行:MultipartFile mlf = rq.getFile(itrator.next());
      MultipartHttpServletRequest からマルチパートファイルを取得します。
      String fileName = mlf.getOriginalFilename();
      マルチパートファイルからファイル名を取得します。
    • 129-143行:アップロードするファイルの拡張子や、ワークブックのシート数などをチェックします。
    • 144-148行:uploadProductData() メソッドで製品一覧をインポートします。
  • 58-108行:uploadProductData() メソッド
    • 60行:Sheet sheet = workbook.getSheetAt(0);
      取得した Excel ファイルのワークブックからシートを取得します。
    • 65行:Row row = sheet.getRow(i);
      シートから行を一行ずつ取り出します。
    • 67行:Iterator cellIterator = row.cellIterator();
      行からセルを取り出します。
    • 68-78行:行が先頭行の場合はヘッダをチェックします
    • 79-105行:データ行を一行ずつ取り出して、セルデータから Product インスタンスを生成して、List<Product> products に追加します。



製品一覧ビュー views/productList.html を次に示します:

  • 6-13行:ImportController.productList() でセットした製品リストが空の場合に表示します。
  • 14-45行:製品リストが空でない場合は製品一覧が表示されます
    Thymeleaf の機能については、「(5)Thymeleaf と連携する」を参照してください。
  • 52-79行:ファイルタイプがエクセルの場合、"dropArea"にドロップされたファイルをアップロードします。
  • 81-108行:実際にアップロード処理を行う uploadFile 関数。
    $.ajax() 関数で、"importProduct"リクエストを行い、成功の場合はページをリフレッシュします。

結果の確認

1.アプリケーションを起動:画面1

アプリケーションを実行して、ブラウザで "localhost:9004/bootImport"を入力します: 製品一覧ビュー views/productList (最初の状態はデータが無い状態)が表示されます:

2.アップロードファイルをドロップ・エリアにドロップ:画面2



ソースコードは GitHub bootImportよりダウンロードできます。

(10)Spring Boot で Web アプリケーションを開発:RESTful サービス

$
0
0
今回はRESTful Web サービス機能を実装します。

プロジェクトの作成

前回同様に、「(1)プロジェクトの作成」と同じ方法で新規プロジェクト bootRest を作成します。

このプロジェクトでは前回までと若干ディレクトリ構造やファイル名を変えました。messages.properties の位置を src/main/resources/messages/ から、src/main/resources/ 、app.properties の名前を application.properties に変更しました。これにより、Spring Boot のデフォルトの位置と名前の設定が使われるため、アプリケーション設定で明示的に設定する必要はありません。設定クラスは非常にシンプルになります。

コントローラの作成


  • 1行:@RestController
    このクラスが、JsonやXMLを返すコントローラであることを示します。
  • 13-16行:"localhost:9005/bootRest/product/getAllProducts"のリクエストに対して製品のマップ Map<Integer, Product> を返します。
    このサンプル・アプリでは、データをデータベースの代わりに ProductsDummyData コンポーネントに保持します。
  • 23-26行:"localhost:9005/bootRest/product/{id}"のリクエスト({id}は具体的な製品id)に応答して指定idの製品を返します。
  • 33-38行:"localhost:9005/bootRest/product/create"のリクエストにより、POST 送信された JSONデータ(製品データ)を追加します。
  • 45-49行:"localhost:9005/bootRest/product/delete/{id}"のリクエスト(DELETE)により指定idの製品を削除します。

このサンプルではデータベースの代わりにデータを保持するクラスを作成します:


ビューの作成

クライアント側の処理を行うビューを作成します:

  • 6-13行:上述のRESTサービスに対する RESTリクエストを用意します。
  • 21-31行:$.ajax() 関数で、url : "/product/delete/1002" , type : "delete"の RESTリクエストを行います。
  • 32-50行:$.ajax() 関数で、url : "/product/create" , type : "post"の RESTリクエストを行います。

結果の確認

1.アプリケーションを起動:画面1

アプリケーションを実行して、ブラウザで "localhost:9005/bootRest"を入力します: ホームビュー views/home が表示されます:



クライアント側の処理1〜4を実行します。
  • 製品一覧を取得します(3件取得)。
  • idが1003の製品を取得します。
  • idが1002の製品を削除して、製品一覧を取得します(2件取得)。
  • 製品 {"id" : 9001, "name" : "製品追加", "price" : 10000} を1件追加して、製品一覧を取得します(3件取得)。


ソースコードは GitHub bootRestよりダウンロードできます。

(11)Spring Boot で Web アプリケーションを開発:Ajax + CSRF対策

$
0
0
このシリーズの(6)bootSecではセキュリティを、(7)bootValidでは検証のサンプルを作成しました。これらは、それぞれ単独では問題なく動きましたが、2つを組み合わせるとどうなるでしょうか? Spring Security では 3.2 から CSRF 対策が組み込まれました(詳細はコチラ)が、Ajax 通信では動作しません。そのため bootValid で $.ajsx() で入力データを POST 送信した場合には、閲覧禁止エラーとなり、アクセスが拒否されます:



今回は、bootSec に bootValid のコードを追加して、さらに Ajax 通信での CSRF 対策を追加します。

プロジェクトの修正

bootSec を次のように修正します:

  • bootsec.controller.FormController を追加
  • bootsec.controller.StatusCmd を追加
  • bootsec.model.Comment を追加
  • bootsec.model.Product を追加
  • bootsec.AppConfig を修正
  • templates/views/valid/form.html を追加して修正(ソースコードは後述)
  • templates/views/valid/results.html を追加
  • templates/views/fragment/frag01.html を修正(ソースコードは後述)
  • templates/messages/messages.properties を修正

CSRF 対策

Spring Security では CSRF 対策として、form に隠し input が自動的に追加されます:
<input type="hidden" name="_csrf" value="xxx" />
form の submit と同様に、ajax 通信でも CSRFトークンを送るように、views/valid/form.html でインクルードする views/fragment/frag01.html :: htmlhead フラグメントを次のように修正します:

  • 7-8行:<meta name="_csrf" th:content="${_csrf.token}"/>
    Thymeleaf の機能を使って meta タグに、${_csrf.token}で取得したCSRFトークンを設定します。
    <meta name="_csrf_header" th:content="${_csrf.headerName}"/>
    meta タグに、${_csrf.headerName}で取得したヘッダ名を設定します。

上述の meta タグから取得したトークンとヘッダ名を ajax 通信時に設定するように、views/valid/form.html を次のように修正します:

  • 8-12行:jQuery の $.ajaxPrefilter() 関数を使って、通信前にリクエストヘッダにCRSFトークンをセットします。

アプリケーションの動作確認

1.製品入力ビューで製品データを保存する(Ajax):画面1

アプリケーションを起動して、ブラウザで "localhost:9001/bootSec"を入力します。
表示されたホームビューで、ナビゲーションバーのメニュー valid をクリックして、製品入力ビュー views/valid/form を表示します。
製品入力ビューで製品データを入力して「保存(Ajax版)」ボタンをクリックします:

アクセスエラーは起きず、製品データが追加されます。

ソースコードは GitHub bootSec:add_validからダウンロードできます。

(12)Spring Boot で Web アプリケーションを開発:一つにまとめる

$
0
0
(1)から(11)で取り上げたトピックを一つにまとめてデモアプリケーションを作成しました。
アプリケーションのソースコードは GitHub : bootHealthからダウンロードできます。
以下に作成したデモアプリケーション(健康管理)の機能を動画で紹介します。

バイタル一覧/血液検査一覧

  • ホーム画面からメニューで"バイタル一覧"を選択します。(デモの設定で、自動的に過去1月分のデータが登録されています)
  • チャート表示ボタンを押して、いくつかのチャートを表示します。
  • 「前」ボタンで前ページへ移動します。
  • 再度、チャート表示ボタンを押してチャートを表示します。
  • 「PDF」ボタンを押してPDFレポートを表示します。
  • 「Excel」ボタンを押してExcel ファイルをダウンロードします。
  • 続いてメニューで”血液検査一覧”を選択します。(デモの設定で、登録データはありません)
  • ドロップエリアへ、xls ファイルをドラッグ&ドロップします。
    エクセルファイルの血液検査データがインポートされます。このとき血液検査マスターが未登録の場合はマスターデータもインポートされます。
  • バイタルの場合と同様に、チャート表示、PDF表示、エクセルファイルのエクスポートを実行します。

iPad で Excel をエクスポートした場合:

バイタル測定値の入力

  • ホーム画面からメニューで"バイタル一覧"を選択します。(まだ当日のバイタルは入力されていません)
  • メニューで"バイタルチェック"を選択します。
  • いくつか測定値を入力して、保存します。
  • メニューで"バイタル一覧"を選択します。(今入力した結果が反映されます)

マスター保守

  • メニューで"保守/ユーザー一覧"を表示します。(デモの設定でユーザーが1件登録済みです)
  • 「追加」ボタンを押して追加処理を行います。(いくつかのデータ検証の例を示します)
  • 追加されたユーザーの「削除」ボタンを押して、追加ユーザーを削除します。
  • "バイタルマスター一覧"、"血液検査マスター一覧"でも同様の操作を行います。
  • 次に"薬マスター一覧"を表示します(デモの設定でデータは登録されていません)
  • バイタル、血液検査と同様にドラッグ&ドロップでデータをインポートします。
  • インポートしたデータからプレドニンを選択して、「編集」ボタンを押します(編集ダイアログが表示されます)
  • データの一部を編集して、「保存」ボタンを押します。(編集結果が反映されます)
  • もう一度プレドニンを選択して編集を元に戻します。
  • プレドニン行の情報検索ボタンを押して、プレドニンの情報を表示します。

iPad でバイタルマスターの編集モーダルを表示した場合:

個人カルテ

  • メニューから"お薬手帳/個人カルテ"を選択します。(1件登録済みのユーザー情報が表示されます)
  • タブを切り替えて表示/編集したい情報を表示します。
  • 「病歴など」タブを選択します。
  • 血液型、病歴などを修正して、「保存」ボタンを押して個人情報を更新します。
  • 「病院」タブを選択します。
  • 「検索」ボタン、「電話」ボタン、「ルート」ボタンを押します。


(1)から(11)で取り上げなかったトピックについて簡単に説明します。
このデモアプリでは、タスクスケジューラとメール送信機能を使用してユーザーに薬服用通知をメールします。

メール機能の実装

  • pom.xml: この機能を使用するには pom.xml に以下を追加する必要があります。
  • メール設定クラス com.itrane.boothealth.MailConfg を作成します:

    上記クラスでは、ap_mail.properties のプロパティティ値を使ってメールの設定を行い、Javaメール送信ビーンを作成します。
  • src/main/resources/ap_mail.properties:

    上記の設定では iCloud smtp を使ってメールを送信します。apple ユーザーの場合は、username, password に apple id とパスワードを設定してください。
    apple ユーザーでない場合はお使いの smtp の設定を行ってください。


デモアプリでは起動時に適当な処方箋データを数件登録しています。この処方箋データに対して、指定したスケジュールでタスクを実行します。

タスクスケジューラ機能の実装

  • pom.xml:特に追加するものはありません
  • com.itrane.boothealth.App クラスに次のアノテーションを追加します。
    @EnableScheduling
  • タスクスケジューラクラスを作成します

上記のスケジュールに従ってメールが指定された送信先アドレスに送信されます。

メールのリンクをクリックすれば RESTリクエストにより、送信済みを設定できます。

Spring Boot で JPAを使用する

$
0
0
Java 永続化 API (JPA)は、Java でオブジェクトを永続化する標準的な方法です。このポストでは、Spring Boot で JPA を使うサンプル・アプリケーションを作成したいと思います。
開発環境は以下の通りです:
  • OS X El Capitan 10.11
  • Maven 3.1.1
  • H2 DB 1.4.188
  • Eclipse Java EE Mars.1 Release
  • Java 1.8.0_60
主な依存ライブラリは以下の通りです:
  • Spring Boot 1.2.6.RELEASE
  • Spring Data JPA 1.7.3.RELEASE
  • EclipseLink 2.6.0

サンプル・プロジェクトの作成

このプロジェクトはドメインモデル(hlbootdb.model パッケージ)、ドメインを操作するリポジトリ(hlbootdb.repo パッケージ)、リポジトリをテストする SpringBootApplication(@SpringBootApplication で注釈したクラス:hlbootdb パッケージ)からなるシンプルなものです。
プロジェクトを作成するために、以下の操作を行います:
  • Eclipse のメインメニューから File -> New -> Maven プロジェクトを選択します。
  • 表示された新規 Maven プロジェクトのウィザードで
    "Create a simple project"をチェックして next をクリック。
  • 表示されたページで以下の内容を設定して finish をクリック。
    Artifact: Group id = com.itrane
    Artifact id = hlBootDB
    Version = (0.0.1)
    Packaging = (jar)
    Name = hlBootDB
  • 作成されたプロジェクトで pom.xml を開いて次のように修正(リスト1):

プロジェクトの作成については、以前のポスト「(1)Spring Boot で Web アプリケーションを開発:プロジェクトの作成」も参照してください。

データベース使用の設定

EclipseLink と H2 DB を使用できるように、hlbootdb.DbConfig クラスで以下の設定を行います(リスト2):
このクラスでは、プロパティファイル(application.properties)のプロパティを使用しています(リスト3):
上記の url プロパティの指定により、h2データベースは、ユーザーのホームディレクトリ下の _data/h2 ディレクトリに作成されます。 DbConfig が同じパッケージ内にある場合は、このデータベース設定は自動的に適用されますが、やりたいことを明確にするために、hlbootdb.App クラスで明示的に @Import しています(リスト4):

ドメインモデルの作成

このサンプル・アプリで使用する3つのエンティティ(顧客、受注、受注明細)を作成します:
顧客 hlbootdb.model.Kokyaku:

受注 hlbootdb.model.Jutyu:

受注明細 hlbootdb.model.Meisai:

上記の3つのクラスは共通の機能を提供するスーパークラス AbstractPersistentEntity クラスを拡張しています。
hlbootdb.model.AbstractPersistentEntitiy:


リポジトリの作成

上記エンティティに対応するリポジトリを作成します。
顧客リポジトリでは、主に単一テーブルに対する処理や命名規約による find メソッドの機能を確認します:
受注リポジトリでは、受注:明細=1:N関連のドメインモデルに対するCRUD処理、@Query を使った複雑な検索の仕方や@Modifying を使ったクエリーによる更新などの機能を調べます:

リポジトリをテストする

上で作成した、リポジトリの各メソッドのテストを行います。この Spring Boot アプリケーションは、Web MVC の機能も GUI も持たない、非常にシンプルなものです(App.java): CommandLineRunner の test() メソッドにより、顧客リポジトリと受注リポジトリのテストを実行します。
顧客リポジトリのテストの内容は以下の通りです:
実行結果は次の通りです:
受注リポジトリのテストは以下の通りです:
実行結果のログ出力は以下のようになります:


H2 のコンソール・ツールを使ってDBを確認する

このサンプルではRDB管理に H2 DBを使用しました。これにより、MySQL などのようにインストール作業をせずに RDBを使用できます。Maven により、開発環境にダウンロードされた h2-xxx.jar を使って、アプリで作成したテーブルや登録データを確認することができます。プロジェクト・エクスプローラで、コンテキストメニューの Show In > Terminal でターミナル・ビューを表示して、以下のコマンドを入力します:
bash-3.2$ cd ~/.m2/repository/com/h2database/h2/1.4.188/
bash-3.2$ ls
_remote.repositories h2-1.4.188.jar.sha1 h2-1.4.188.pom.sha1
h2-1.4.188.jar h2-1.4.188.pom
bash-3.2$ java -cp h2-1.4.188.jar org.h2.tools.Console
ブラウザに次の画面が表示されます:
application.properties で設定した url, username, password プロパティの値を入力して "Connect"ボタンを押して表示された画面で、DBの内容を確認することができます:

まとめ

リポジトリやサービス・コンポーネントで EntityManager を取得して、すべてを JPA の機能を使って行うこともできます。このサンプル・アプリケーションでは、Spring Data JPA がサポートする機能だけを使って、簡単なデータの追加・更新・削除と、ある程度複雑な検索を行いました。

ソースコード: GitHub hlBootDBからダウンロードできます。

Spring MVC + Thymeleaf による WEB アプリケーション開発(6)新規、編集フォームを追加する

$
0
0
前回は、店舗一覧ビューを修正して、各行に"編集"および "削除"ボタンを、一覧テーブルの下部には "店舗追加"ボタンを追加しました。今回はこれらのボタンが押されたときに、店舗の追加/編集を行うためのフォームを表示できるようにします。

ダイアログフラグメントの作成
WEB-INF/views/fragment/frag01.htmlに Bootstrap の Modal を利用して、店舗新規追加フォーム、選択店舗編集フォームを持つダイアログのフラグメントを追加します。
WEB-INF/views/fragment/frag01.html ::  shop_create_modal:
...
<!-- Shop Create Modal -->
<div th:fragment="shop-create-modal">
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button"class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3 class="modal-title" id="myModalLabel">タイトル</h3>
</div>
<div class="modal-body">

<form class="form-horizontal" role="form" action="#" th:action="@{/mvc/edit}"
th:object="${shop}" method="post">
<fieldset>
<div class="form-group">
<label class="col-sm-2 control-label"for="name">店舗名</label>
<div class="col-sm-10 controls">
<input type="text" th:field="*{name}" placeholder="店舗名"
th:errorclass="fieldError" maxlength="20"class="form-control" />
<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}">不正な入力</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"for="phone">電話番号</label>
<div class="col-sm-10 controls">
<input type="text" th:field="*{phone}" placeholder="電話番号"
th:errorclass="fieldError" maxlength="13"class="form-control" />
<span th:if="${#fields.hasErrors('phone')}" th:errors="*{phone}">不正な入力</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"for="email">メール</label>
<div class="col-sm-10 controls">
<input type="text" th:field="*{email}" placeholder="メールアドレス"
th:errorclass="fieldError" maxlength="50"class="form-control" />
<span th:if="${#fields.hasErrors('email')}" th:errors="*{email}">不正な入力</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"for="kaitenBi">開店日</label>
<div class="col-sm-10 controls">
<input type="text" th:field="*{kaitenBi}"
th:errorclass="fieldError" maxlength="13"class="form-control" />
<span th:if="${#fields.hasErrors('kaitenBi')}" th:errors="*{kaitenBi}">不正な入力</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"for="emplNumber">従業員数</label>
<div class="col-sm-10 controls">
<input type="text" th:field="*{emplNumber}"
th:errorclass="fieldError" maxlength="3"class="form-control" />
<span th:if="${#fields.hasErrors('emplNumber')}" th:errors="*{emplNumber}">不正な入力</span>
</div>
</div>
</fieldset>
</form>

</div>
<div class="modal-footer">
<button type="button"class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button"class="btn btn-primary">保存</button>
</div>
</div>
</div>
</div>
</div>
...

上記のコードで、フォームのスタイルを決定する様々な CSS クラスは、Bootstrap の CSS (例:"modal-dialog", "form-horizontal", "form-group" など)を使用しています。<form> タグでは、SpringMVC と連携するために、Thymeleaf の属性(例:th:object, th:action など)を使用します。th:object 属性は、フォーム・バッキングビーンとして、ユーザー入力を受け取るために Shop オブジェクトを使用することを指定します。<input> タグの th:field 属性は Shop オブジェクトのフィールドを *{...} 式(例: *{name}, *{phone} など)で指定します。

コントローラー(ShopController)の修正
ShopContoller#shopList() メソッドを修正して、shop_list.html(店舗一覧ビュー)で Shop オブジェクト(モデル)を参照できるよう、ModelAndView に追加します。さらに店舗一覧ビューで"削除"ボタンが押された場合に呼び出されるメソッド deleteShop() と "編集"ボタンが押された場合に呼ばれるメソッド editShop() も追加します。
修正された com.itrane.controller.ShopController :
    ...
/**
* 店舗一覧ビューを表示する.
* @return ModelAndView: 表示ビューは shop_list.html
*/

@RequestMapping(value = "/list")
public ModelAndView shopList() {
logger.debug("");
ModelAndView mav = new ModelAndView("/shop/shop_list");
Shop shop = new Shop();
mav.addObject("shop", shop);
return mav;
}

...
@RequestMapping(value="/delete/{id}",
produces="text/html;charset=UTF-8", method=RequestMethod.GET)
public @ResponseBody String deleteShop(@PathVariable Integer id) {

logger.debug("id=" + id);
ModelAndView mav = new ModelAndView("redirect:/shop/list.html");
//Shop shop = shopService.delete(id);
String message = "店舗 を削除しました.";
return toJson(message);
}

@RequestMapping(value = "/edit/{id}",
produces="text/html;charset=UTF-8", method=RequestMethod.GET)
public @ResponseBody String editShop(@PathVariable Integer id) {
Shop shop = shopService.findById(id);
logger.debug("id=" + id + " shop.id=" + shop.getId() + " name=" + shop.getName());
return toJson(shop);
}
...


店舗一覧ビュー(shop_list.html)の javascript パートの修正
前回、ボタンを追加するために修正した javascript パートを次のように修正します。
WEB-INF/views/shop/shop_list.html(javascript パート):
(編集ボタンが押された場合の処理)
    ... 
var be = $('<button class="btn_edit">編集</button>');
be.button();
be.on('click',function(){
fBtn = true;
//ajax で get して成功時はダイアログを表示
$.getJSON("/mvcdemo/shop/edit/" + cellData + ".html", {},
function(shop){
$("#myModalLabel").html("店舗 (id="+cellData+") を修正してください");
setShop(shop.name, shop.phone, shop.email,
shop.kaitenBi, shop.emplNumber);
$('#myModal').modal('show');
});
});
...
jQuery の getJSON で ShopController#editShop( ) (リクエスト:"mvcdemo/shop/edit/{id}")を呼び出します。リクエストが成功した場合は、editShop( ) から、選択した id に対応する店舗データが JSON 形式で返されます(成功時に実行する関数に shop 引数として渡されます)。 この shop データを setShop(...) 関数でフォームの各フィールドにセットして、ダイアログを表示します。ダイアログおよびフォームは、上述のフラグメント(frg01.html :: shop-create-modal)を使用します。
(店舗追加ボタンが押された場合の処理)
    ...
$("#shop-create").click(function () {
$("#myModalLabel").html("新規店舗を追加してください。");
setShop("", "", "", "", "");
$('#myModal').modal('show');
});
...
編集の場合と同様に、上述のフラグメント(frag01.html :: shop-create-modal)を使用します。setShop(...) 関数でフォームの各フィールドをクリアした後、ダイアログを表示します。
上の2つの処理で呼んでいる setShop( ) 関数も追加します。
    ...
function setShop(name, phone, email, kaitenBi, emplNumber) {
$("#name").val(name);
$("#phone").val(phone);
$("#email").val(email);
$("#kaitenBi").val(kaitenBi);
$("#emplNumber").val(emplNumber);
}
...


店舗一覧ビュー(shop_list.html)の HTML パートの修正
最後にダイアログのフラグメント(frag01.html: shop-create-modal) を shop_list.html にインクルードするように、shop_list.html の HTML パートを次のように修正します。
WEB-INF/views/shop/shop_list.html:
...
</div> <!-- Container END -->
</div> <!-- Contents END -->

<div th:include="fragment/frag01 :: shop-create-modal"></div>

</body>
</html>

修正結果の確認
(店舗追加ボタンを押した場合)

(2ページ目の3行目の編集ボタンを押した場合)


追加フォーム、編集フォームは同じフラグメントを共用しています。このフォームでは保存ボタンが押された時、追加、編集のどちらのモードかを判定して、モード別にコントローラの追加メソッド、更新メソッドを呼び出してデータを登録/更新して、一覧画面に戻る処理を追加する必要があります。登録/更新処理は ShopService のメソッドを使えば簡単に実装できますが、一覧画面のどのページに戻すかはアプリの要件によります。このデモアプリでは保存ボタン押下後の処理は実装していません。この処理は次回以降のシリーズ(実用アプリの作成)で紹介したいと思います。

まとめ
今回のシリーズでは、SpringMVC と Tymeleaf, jQuery, Bootstrap などのフレームワークを使ってWEBアプリを作成しました。そして次のテーマを紹介しました。
  1. 各フレームワークを使用するための雛形となるプロジェクトの作成
  2. データベースアクセスのための設定
  3. モデルの作成
  4. 一覧ビューの作成と改良
  5. 編集フォームの作成
次回シリーズでは、同じフレームワークを利用して少し実用的なアプリを作成したいと思います。実用的なアプリでは次のテーマを扱いたいと思っています。
  1. JPA実装に EclipseLink を使った場合の pom.xml の作成
  2. JPA実装に EclipseLink を使った場合のデータベース設定
  3. enum 型や1:多関連のエンティティの作成
  4. 実用アプリでの一覧ビューやフォームの作成
  5. 入力データの検証
  6. エンドユーザーコンピューティング: CSV, エクセル形式でのデータのダウンロード
  7. 入力データを元にしたレポート(PDFやグラフ)の作成
  8. モバイル端末からのデータアクセス
  9. モバイル端末への通知

ソースコード
ソースは branch:mvcdemo06からダウンロードできます。


Spring MVC + Thymeleaf による WEB アプリケーション開発(5)一覧ビューの変更

$
0
0
今回は前回作成した一覧ビューを変更して、テーブルの各行に編集ボタン、削除ボタンを追加します。テーブルの下に店舗追加ボタンも追加します。その他にもいくつかの変更を行います。

バージョンの変更
HTMLヘッダ・フラグメントの (frag01.html) を修正して、jQuery, dataTables のバージョンをそれぞれ、以下のように変更します。
WEB-INF/views/frag01.html (htmlhead フラグメント):
...
<!-- HTMLヘッダ -->
<head th:fragment="htmlhead">
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title th:text="${title}">(title)</title>

<!-- CSS -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" />
<link rel="stylesheet" href="//cdn.datatables.net/1.10.1/css/jquery.dataTables.css" />

<!-- HTML5 shim, IE6-8で HTML5 要素をサポートする -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->

<!-- JavaScript : jquery, bootstrap, custom -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="//cdn.datatables.net/1.10.1/js/jquery.dataTables.js"></script>
</head>
...



編集ボタンと削除ボタンの追加
店舗一覧テーブルの各行に編集、削除のためのボタンを追加します。shop_list.htmlの HTMLパートを以下のように変更して、ボタンを表示する"操作"列を追加します。
WEB-INF/views/shop/shop_list.html (HTMLパート):
   ... 
<table id="shops"class="display">
<thead>
<tr>
<th>id</th>
<th>店舗名</th>
<th>電話番号</th>
<th>メール</th>
<th>開店日</th>
<th>従業員数</th>
<th>操作</th>
</tr>
</thead>
...

さらに、javascript パートを以下のように変更します。
WEB-INF/views/shop/shop_list.html (javascript パート):
...
"aoColumns": [
{"sTitle": "ID", "mData": "id", "sWidth": 40, "sClass": "center" },
{"sTitle": "店舗名", "mData": "name", "sWidth": 200 },
{"sTitle": "電話番号", "mData": "phone", "sWidth": 70 },
{"sTitle": "メール", "mData": "email", "sWidth": 240 },
{"sTitle": "開店日", "mData": "kaitenBi", "sWidth": 60 },
{"sTitle": "従業員数", "mData": "emplNumber", "sWidth": 100, "sClass": "alignRight",
"fnCreatedCell": function (td, cellData, rowData, row, col) {
if ( cellData > 15 ) {
$(td).css('color', 'red');
}
}
},
{
"mData": "id", "sClass": "center", "sWidth": 100, "bSortable": false,
"fnCreatedCell": function (td, cellData, rowData, row, col) {
var bd = $('<button class="btn_delete">削除</button>');
bd.button();
bd.on('click',function(){
fBtn = true;
alert("Delete:"+cellData);
});
var be = $('<button class="btn_edit">編集</button>');
be.button();
be.on('click',function(){
fBtn = true;
alert("Edit:"+cellData);
});
$(td).empty();
$(td).prepend(bd);
$(td).prepend(be);
}
}
],
...
上記のコードでは列定義の "aoColumns"に"操作"列の定義を追加しています。列生成関数 "fnCreatedCell"で、"削除"ボタンと"編集"ボタンを生成し、ボタンのクリック時の関数を定義します。列生成関数の引数 cellData で該当行の店舗データの id が渡されます。現時点では、各ボタンのクリック時には alert でクリックされた行の id を表示します。

複数行選択機能の追加
表示ページの複数行を選択できるようにします。
WEB-INF/views/shop/shop_list.html (javascript パート):
    ...
$('#shops tbody').on( 'click', 'tr', function () {
if (fBtn==false) {
$(this).toggleClass('selected');
} else {
fBtn = false;
}
} );
...
行がクリックされたときに、CSS のクラス名 'selected'を追加(または削除)します。fBtn 変数は、ボタンが押された場合を除外するために使用しています。

店舗追加ボタンの追加
テーブルの下に"店舗追加"ボタン(BootStrap版)を追加します。
WEB-INF/views/shop/shop_list.html (HTMLパート):
  ...
<div class="table-container">
<table id="shops"class="display">
...
</table>
<br /><br />
</div>

<button id="shop-create"class="btn btn-primary btn-lg">店舗追加</button>
<!-- $$$ Contents Footer $$$$$ -->
<div th:include="fragment/frag01 :: footer"></div>
...


変更結果の確認
デモアプリケーションを実行して、変更の結果を確認します。
(ボタン追加とバージョン変更の確認)
メニューバーで、店舗管理 > 店舗一覧を選択します:
"操作"列が追加され、各行に"編集"ボタンと"削除"ボタンが追加され、またテーブルの下方には"店舗追加"ボタンが追加されます。

(複数行選択の確認)
上記画面で、3ページ目へ移動して、dataTables のページング機能を確認します。任意の行をいくつかクリックして選択します:

(ボタン押下時の動作を確認)
2行目 (id=2022) の編集ボタンをクリッックします:


次回は”店舗追加”ボタン、”編集”ボタンが押された時、入力フォームを表示して、店舗データの追加、修正が行えるようにします。

ソースコード
ソースコードは branch:mvcdemo05Aからダウンロードできます。

Viewing all 71 articles
Browse latest View live