かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その9 )( 各画面の HTML を作成する2 )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その8 )( 各画面の HTML を作成する ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • 各画面の HTML を作成します。
    • 今回は入力画面2、入力画面3です。

参照したサイト・書籍

目次

  1. 入力画面2の HTML を作成する
  2. 入力画面3の HTML を作成する

手順

入力画面2の HTML を作成する

static/inquiry/input02.html を新規作成します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>入力フォーム - 入力画面2</title>
  <!-- Tell the browser to be responsive to screen width -->
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
  <!-- Bootstrap 3.3.6 -->
  <link rel="stylesheet" href="/vendor/bootstrap/css/bootstrap.min.css">
  <!-- Font Awesome -->
  <link rel="stylesheet" href="/vendor/font-awesome/css/font-awesome.min.css">
  <!-- Ionicons -->
  <link rel="stylesheet" href="/vendor/ionicons/css/ionicons.min.css">
  <!-- Theme style -->
  <link rel="stylesheet" href="/vendor/admin-lte/css/AdminLTE.min.css">
  <link rel="stylesheet" href="/vendor/admin-lte/css/skins/skin-blue.min.css">

  <!-- 画面共通で使用する css -->
  <link rel="stylesheet" href="/css/common.css">
</head>

<body class="skin-blue layout-top-nav">
<div class="wrapper">

  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    <section class="content-header">
      <h1>
        入力画面2
      </h1>
    </section>

    <!-- Main content -->
    <section class="content">
      <div class="row">
        <div class="col-xs-12">
          <form id="input02Form" class="form-horizontal" method="post" action="">
            <!-- 郵便番号 -->
            <div class="form-group" id="form-group-zipcode">
              <div class="control-label col-sm-2">
                <label class="float-label">郵便番号</label>
                <div class="label label-required">必須</div>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <p class="form-control-static-inline"></p>
                  <input type="text" name="zipcode1" id="zipcode1" class="form-control form-control-inline" style="width: 60px;" value="" placeholder=""/>
                  <p class="form-control-static-inline">-</p>
                  <input type="text" name="zipcode2" id="zipcode2" class="form-control form-control-inline" style="width: 80px;" value="" placeholder=""/>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- 住所 -->
            <div class="form-group" id="form-group-address">
              <div class="control-label col-sm-2">
                <label class="float-label">住所</label>
                <div class="label label-required">必須</div>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <input type="text" name="address" id="address" class="form-control" value="" placeholder="例)東京都千代田区飯田橋1-1"/>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- 電話番号 -->
            <div class="form-group" id="form-group-tel">
              <div class="control-label col-sm-2">
                <label class="float-label">電話番号</label>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <input type="text" name="tel1" id="tel1" class="form-control form-control-inline" style="width: 100px;" value="" placeholder=""/>
                  <p class="form-control-static-inline">-</p>
                  <input type="text" name="tel2" id="tel2" class="form-control form-control-inline" style="width: 100px;" value="" placeholder=""/>
                  <p class="form-control-static-inline">-</p>
                  <input type="text" name="tel3" id="tel3" class="form-control form-control-inline" style="width: 100px;" value="" placeholder=""/>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- メールアドレス -->
            <div class="form-group" id="form-group-email">
              <div class="control-label col-sm-2">
                <label class="float-label">メールアドレス</label>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <input type="text" name="email" id="email" class="form-control" style="width: 250px;" value="" placeholder="例)taro.tanaka@sample.co.jp"/>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <div class="text-center">
              <br/><p class="text-primary text-bold">※電話番号とメールアドレスのいずれか一方は必ず入力してください。</p><br/>
              <button class="btn bg-blue js-btn-back"><i class="fa fa-arrow-left"></i> 前の画面へ戻る</button>
              <button class="btn bg-green js-btn-next" disabled><i class="fa fa-arrow-right"></i> 次へ</button>
            </div>
          </form>
        </div>
      </div>
    </section>
    <!-- /.content -->
  </div>
  <!-- /.content-wrapper -->
</div>
<!-- ./wrapper -->

<!-- REQUIRED JS SCRIPTS -->
<script src="/js/inquiry/input02.js"></script>

</body>
</html>

画面の初期表示は以下のようになります。

f:id:ksby:20170729173851p:plain f:id:ksby:20170729231148p:plain

入力画面3の HTML を作成する

static/inquiry/input03.html を新規作成します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>入力フォーム - 入力画面3</title>
  <!-- Tell the browser to be responsive to screen width -->
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
  <!-- Bootstrap 3.3.6 -->
  <link rel="stylesheet" href="/vendor/bootstrap/css/bootstrap.min.css">
  <!-- Font Awesome -->
  <link rel="stylesheet" href="/vendor/font-awesome/css/font-awesome.min.css">
  <!-- Ionicons -->
  <link rel="stylesheet" href="/vendor/ionicons/css/ionicons.min.css">
  <!-- Theme style -->
  <link rel="stylesheet" href="/vendor/admin-lte/css/AdminLTE.min.css">
  <link rel="stylesheet" href="/vendor/admin-lte/css/skins/skin-blue.min.css">

  <!-- 画面共通で使用する css -->
  <link rel="stylesheet" href="/css/common.css">
  <style>
    /* 「チェックボックス複数行」の入力項目のチェックボックスを複数行に書くので、 */
    /* 異なる行のチェックボックスの位置を左揃えにする                         */
    @media (min-width: 768px) {
      #multiline-checkbox .checkbox label {
        display: block;
        float: left;
        width: 180px;
      }
    }
    @media (max-width: 767px) {
      #multiline-checkbox .checkbox label {
        display: block;
        float: left;
        width: 100%;
      }
    }
  </style>
</head>

<body class="skin-blue layout-top-nav">
<div class="wrapper">

  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    <section class="content-header">
      <h1>
        入力画面3
      </h1>
    </section>

    <!-- Main content -->
    <section class="content">
      <div class="row">
        <div class="col-xs-12">
          <form id="input03Form" class="form-horizontal" method="post" action="">
            <!-- お問い合わせの種類1 -->
            <div class="form-group" id="form-group-type1">
              <div class="control-label col-sm-2">
                <label class="float-label">お問い合わせの種類1</label>
                <div class="label label-required">必須</div>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <select name="type1" id="type1" class="form-control" style="width: 250px;">
                    <option value="">選択してください</option>
                    <option value="1">製品に関するお問い合わせ</option>
                    <option value="2">人事・採用情報</option>
                    <option value="3">その他</option>
                  </select>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- お問い合わせの種類2 -->
            <div class="form-group" id="form-group-type2">
              <div class="control-label col-sm-2">
                <label class="float-label">お問い合わせの種類2</label>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <div class="checkbox">
                    <label><input type="checkbox" name="type2">見積が欲しい</label>
                    <label><input type="checkbox" name="type2">資料が欲しい</label>
                    <label><input type="checkbox" name="type2">その他の問い合わせ</label>
                  </div>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- お問い合わせの内容 -->
            <div class="form-group" id="form-group-inquiry">
              <div class="control-label col-sm-2">
                <label class="float-label">お問い合わせの内容</label>
                <div class="label label-required">必須</div>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <textarea rows="5" name="inquiry" id="inquiry" class="form-control" placeholder="お問い合わせ内容を入力して下さい"></textarea>
                </div></div>
                <div class="row"><div class="col-sm-10"><p class="form-control-static"><small>※最大500文字</small></p></div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <div class="form-group" id="form-group-survey">
              <div class="control-label col-sm-2">
                <label class="float-label">アンケート</label>
              </div>
              <div class="col-sm-10" id="multiline-checkbox">
                <div class="row"><div class="col-sm-12">
                  <div class="checkbox">
                    <label><input type="checkbox" name="survey" value="1">選択肢1だけ長くしてみる</label>
                    <label><input type="checkbox" name="survey" value="2">選択肢2</label>
                    <label><input type="checkbox" name="survey" value="3">選択肢3</label>
                  </div>
                </div></div>
                <div class="row"><div class="col-sm-12">
                  <div class="checkbox">
                    <label><input type="checkbox" name="survey" value="4">選択肢4</label>
                    <label><input type="checkbox" name="survey" value="5">選択肢5が少し長い</label>
                    <label><input type="checkbox" name="survey" value="6">選択肢6</label>
                  </div>
                </div></div>
                <div class="row"><div class="col-sm-12">
                  <div class="checkbox">
                    <label><input type="checkbox" name="survey" value="7">選択肢7</label>
                    <label><input type="checkbox" name="survey" value="8">選択肢8</label>
                    <label><input type="checkbox" name="survey" value="9"></label>
                  </div>
                </div></div>
              </div>
            </div>

            <div class="text-center">
              <button class="btn bg-blue js-btn-back"><i class="fa fa-arrow-left"></i> 前の画面へ戻る</button>
              <button class="btn bg-green js-btn-confirm" disabled><i class="fa fa-arrow-right"></i> 確認画面へ</button>
            </div>
          </form>
        </div>
      </div>
    </section>
    <!-- /.content -->
  </div>
  <!-- /.content-wrapper -->
</div>
<!-- ./wrapper -->

<!-- REQUIRED JS SCRIPTS -->
<script src="/js/inquiry/input03.js"></script>

</body>
</html>

画面の初期表示は以下のようになります。

f:id:ksby:20170729230101p:plain f:id:ksby:20170729231008p:plain

履歴

2017/07/29
初版発行。
2017/08/08
* 入力画面2の form タグの id を input01Forminput02Form へ変更しました。
* 入力画面3の form タグの id を input01Forminput03Form へ変更しました。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その8 )( 各画面の HTML を作成する )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その7 )( webpack + browser-sync をインストールする ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • 各画面の HTML を作成します。
    • 今回は入力画面1だけです。2~3回に分ける予定です。

参照したサイト・書籍

  1. Bootstrap3でFormを簡単に作る方法
    http://tetra-themes.com/bootstrap3-form-197/

  2. Bootstrap3日本語リファレンス - CSS - フォーム - サポートしているフォーム部品
    http://bootstrap3.cyberlab.info/css/forms-supportedControls.html

  3. Form inline inside a form horizontal in twitter bootstrap?
    https://stackoverflow.com/questions/12201835/form-inline-inside-a-form-horizontal-in-twitter-bootstrap

  4. リキッド・レイアウトでチェックボックスを綺麗に並べる
    http://webos-goodies.jp/archives/51275332.html

  5. npm-scripts で作るフロントエンド環境の構築
    http://ceblog.mediba.jp/post/154522336717/npm-scripts

目次

  1. 画面の仕様を決める
  2. starter.html からテンプレートに使用する template.html を作成する
  3. src/main/assets/css の下で更新された css ファイルを自動で src/main/resources/static/css の下に反映する仕組みを作成する
  4. 画面共通で使用する css ファイルを作成する
  5. 入力画面1の HTML を作成する

手順

画面の仕様を決める

「Spring Boot + npm + Geb で入力フォームを作ってテストする」は Geb でのテストのサンプルを作成することが目的なので、いろいろ要素を盛り込みます。

  • 入力項目には以下の要素を入れます。
    • テキストボックス(<input type="text" ..."
    • ラジオボタン<input type="radio" ..."
    • チェックボックス<input type="checkbox" ..."
    • テキストエリア(<input type="textarea" ..."
  • 画面の入力チェックは Javascript で行います。入力チェックエラー時にはラベルと入力フィールドを赤く表示し、入力項目の下にエラーメッセージが表示されるようにします。
  • Ajax でデータを送受信する仕組みも入れます。「郵便番号」を入力したら「住所」に都道府県・市区町村・町丁目名がセットされるようにします。郵便番号検索API を利用する想定です。

starter.html からテンプレートに使用する template.html を作成する

作業開始前にコマンドプロンプトnpm run server コマンドを実行して browser-sync を起動しておきます。

node_modules/admin-lte/starter.html を static の下にコピーし、ファイル名を template.html に変更した後、以下の点を変更します。

  • header タグ内の以下の部分を変更します。
    • <title>AdminLTE 2 | Starter</title><title>入力フォーム</title>
    • <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"><link rel="stylesheet" href="/vendor/bootstrap/css/bootstrap.min.css">
    • <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css"><link rel="stylesheet" href="/vendor/font-awesome/css/font-awesome.min.css">
    • <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css"><link rel="stylesheet" href="/vendor/ionicons/css/ionicons.min.css">
    • <link rel="stylesheet" href="dist/css/AdminLTE.min.css"><link rel="stylesheet" href="/vendor/admin-lte/css/AdminLTE.min.css">
    • <link rel="stylesheet" href="dist/css/skins/skin-blue.min.css"><link rel="stylesheet" href="/vendor/admin-lte/css/skins/skin-blue.min.css">
    • 以下の2行は削除します。
      • <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      • <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
  • <body class="skin-blue layout-top-nav"><body class="skin-blue layout-top-nav"> に変更します。
  • body タグ内の以下の部分は削除します。
    • <!-- Main Header --><header class="main-header"> ... </header> の部分
    • <!-- Left side column. contains the logo and sidebar --><aside class="main-sidebar"> ... </aside> の部分
    • <!-- Main Footer --><footer class="main-footer"> ... </footer> の部分
    • <!-- Control Sidebar --><aside class="control-sidebar control-sidebar-dark"> ... </aside> の部分
    • <!-- Add the sidebar's background. This div must be placed immediately after the control sidebar --><div class="control-sidebar-bg"></div> の部分
    • js ファイルを読み込んでいる以下の3行。
      • <script src="plugins/jQuery/jquery-2.2.3.min.js"></script>
      • <script src="bootstrap/js/bootstrap.min.js"></script>
      • <script src="dist/js/app.min.js"></script>
  • <section class="content-header"> ... </section> 内の以下の部分を削除します。
    • <small>Optional description</small>
    • <ol class="breadcrumb"> ... </ol> の部分
  • あとはコメントで残す必要がないものも全て削除します。

ここまでの変更で template.html は以下のようになります。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>入力フォーム</title>
  <!-- Tell the browser to be responsive to screen width -->
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
  <!-- Bootstrap 3.3.6 -->
  <link rel="stylesheet" href="/vendor/bootstrap/css/bootstrap.min.css">
  <!-- Font Awesome -->
  <link rel="stylesheet" href="/vendor/font-awesome/css/font-awesome.min.css">
  <!-- Ionicons -->
  <link rel="stylesheet" href="/vendor/ionicons/css/ionicons.min.css">
  <!-- Theme style -->
  <link rel="stylesheet" href="/vendor/admin-lte/css/AdminLTE.min.css">
  <link rel="stylesheet" href="/vendor/admin-lte/css/skins/skin-blue.min.css">
</head>

<body class="hold-transition skin-blue sidebar-mini">
<div class="wrapper">

  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    <section class="content-header">
      <h1>
        Page Header
      </h1>
    </section>

    <!-- Main content -->
    <section class="content">

      <!-- Your Page Content Here -->

    </section>
    <!-- /.content -->
  </div>
  <!-- /.content-wrapper -->
</div>
<!-- ./wrapper -->

<!-- REQUIRED JS SCRIPTS -->

</body>
</html>

http://localhost:9080/template.html にアクセスして画面を表示すると以下の画像のようになります。

f:id:ksby:20170726233139p:plain

Ctrl+C を押して npm run server コマンドを一旦停止します。

src/main/assets/css の下で更新された css ファイルを自動で src/main/resources/static/css の下に反映する仕組みを作成する

src/main/assets/css の下に common.css を新規作成し、変更されたら自動で src/main/resources/static/css の下にコピーする仕組みを作成します。変更の検知は cpx の watch 機能を利用します。

src/main/assets の下に css ディレクトリを新規作成し、その下に common.css を新規作成します。この時点では common.css の中身は空です。

f:id:ksby:20170727012330p:plain

package.json の scripts を以下のように変更します。

  "scripts": {
    ..........,
    "copy:css:watch": "cpx src/main/assets/css/**/* src/main/resources/static/css -w",
    "server": "run-p copy:css:watch webpack:watch browser-sync:start"
  },
  • "copy:css:watch": "cpx src/main/assets/css/**/* src/main/resources/static/css -w" を追加します。
  • "server"copy:css:watch を追加します。

static/template.html を以下のように変更します。

<head>
  ..........

  <!-- 画面共通で使用する css -->
  <link rel="stylesheet" href="/css/common.css">
</head>
  • <link rel="stylesheet" href="/css/common.css"> を追加し、src/main/resources/static/css/common.css を読み込むようにします。

以上で準備は完了です。common.css を変更したら自動でブラウザに反映されるか確認します。

npm run server コマンドを実行します。

f:id:ksby:20170727013247p:plain f:id:ksby:20170727013350p:plain

src/main/resources/static の下に css/common.css がコピーされます。

f:id:ksby:20170727013931p:plain

http://localhost:9080/template.html にアクセスすると以下の画面が表示されます。

f:id:ksby:20170727013518p:plain

src/main/assets/css/common.css に背景色を変更する以下の記述を追加します。

/* body 内の背景色を白より少し色がついた色にする */
.content-wrapper {
    background-color: ivory;
}

browser-sync が common.css の変更を検知したことを示すログを出力し、

f:id:ksby:20170727014127p:plain

ブラウザに表示している画面の背景色も手動リロードすることなく変わりました。

f:id:ksby:20170727014229p:plain

画面共通で使用する css ファイルを作成する

src/main/assets/css/common.css に以下の内容を記述します。

/* body 内の背景色を白より少し色がついた色にする */
.content-wrapper {
    background-color: ivory;
}
/* フォーカス時の入力フィールドの色合いが adminLTE より Bootstrap のものが好みなので、そちらに定義し直す */
.form-control:focus {
    border-color: #66afe9;
    outline: 0;
    -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6);
    box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6);
}
/* form-control-static の文字列を入力項目のすぐ下に隙間をほとんど空けずに表示されるようにする */
.form-control-static {
    padding-top: 0px;
    padding-bottom: 0px;
    margin-bottom: -15px;
}
/* 入力チェックエラーの入力項目の背景色を薄い赤色にする */
.has-error .form-control {
    background-color: #fff5ee;
}
/* form-horizontal 内で form-control, form-control-static-inline の入力フィールドを横並びで表示する */
.form-control-inline {
    float:left;
    margin-right: 10px;
}
.form-control-static-inline {
    float: left;
    padding-top: 7px;
    margin-right: 10px;
}
/* checkbox の文字列の右側に少し隙間を空ける */
.checkbox label {
    margin-right: 10px;
}
/* 必須ラベル */
/* 767px 以下の場合には必須ラベルを右側に移動する */
@media (min-width: 768px) {
    .label-required {
        background-color: red;
        margin-right: 5px;
    }
    .form-group .float-label {
        float: right;
    }
}
@media (max-width: 767px) {
    .label-required {
        background-color: red;
        margin-left: 5px;
    }
}
/* 画面下のボタンを大きく表示させる */
.btn {
    width: 150px;
}

入力画面1の HTML を作成する

static の下に inquiry ディレクトリを作成した後、static/template.html をコピーして static/inquiry/input01.html を作成します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>入力フォーム - 入力画面1</title>
  <!-- Tell the browser to be responsive to screen width -->
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
  <!-- Bootstrap 3.3.6 -->
  <link rel="stylesheet" href="/vendor/bootstrap/css/bootstrap.min.css">
  <!-- Font Awesome -->
  <link rel="stylesheet" href="/vendor/font-awesome/css/font-awesome.min.css">
  <!-- Ionicons -->
  <link rel="stylesheet" href="/vendor/ionicons/css/ionicons.min.css">
  <!-- Theme style -->
  <link rel="stylesheet" href="/vendor/admin-lte/css/AdminLTE.min.css">
  <link rel="stylesheet" href="/vendor/admin-lte/css/skins/skin-blue.min.css">

  <!-- 画面共通で使用する css -->
  <link rel="stylesheet" href="/css/common.css">
</head>

<body class="skin-blue layout-top-nav">
<div class="wrapper">

  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    <section class="content-header">
      <h1>
        入力画面1
      </h1>
    </section>

    <!-- Main content -->
    <section class="content">
      <div class="row">
        <div class="col-xs-12">
          <form id="input01Form" class="form-horizontal" method="post" action="">
            <!-- お名前(漢字) -->
            <div class="form-group" id="form-group-name">
              <div class="control-label col-sm-2">
                <label class="float-label">お名前(漢字)</label>
                <div class="label label-required">必須</div>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <input type="text" name="lastname" id="lastname" class="form-control form-control-inline" style="width: 150px;" value="" placeholder="例)田中"/>
                  <input type="text" name="firstname" id="firstname" class="form-control form-control-inline" style="width: 150px;" value="" placeholder="例)太郎"/>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- お名前(かな) -->
            <div class="form-group" id="form-group-kana">
              <div class="control-label col-sm-2">
                <label class="float-label">お名前(かな)</label>
                <div class="label label-required">必須</div>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <input type="text" name="lastkana" id="lastkana" class="form-control form-control-inline" style="width: 150px;" value="" placeholder="例)たなか"/>
                  <input type="text" name="firstkana" id="firstkana" class="form-control form-control-inline" style="width: 150px;" value="" placeholder="例)たろう"/>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- 性別 -->
            <div class="form-group" id="form-group-sex">
              <div class="control-label col-sm-2">
                <label class="float-label">性別</label>
                <div class="label label-required">必須</div>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <div class="radio-inline"><label><input type="radio" name="sex" value="1">男性</label></div>
                  <div class="radio-inline"><label><input type="radio" name="sex" value="2">女性</label></div>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- 年齢 -->
            <div class="form-group" id="form-group-age">
              <div class="control-label col-sm-2">
                <label class="float-label">年齢</label>
                <div class="label label-required">必須</div>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <input type="text" name="age" id="age" class="form-control form-control-inline" style="width: 150px;" value="" placeholder="例)30"/>
                  <p class="form-control-static-inline"></p>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- 職業 -->
            <div class="form-group" id="form-group-job">
              <div class="control-label col-sm-2">
                <label class="float-label">職業</label>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <select name="job" id="job" class="form-control" style="width: 250px;">
                    <option value="">選択してください</option>
                    <option value="1">会社員</option>
                    <option value="2">学生</option>
                    <option value="3">その他</option>
                  </select>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <div class="text-center">
              <button class="btn bg-green js-btn-next" disabled><i class="fa fa-arrow-right"></i> 次へ</button>
            </div>
          </form>
        </div>
      </div>
    </section>
    <!-- /.content -->
  </div>
  <!-- /.content-wrapper -->
</div>
<!-- ./wrapper -->

<!-- REQUIRED JS SCRIPTS -->
<script src="/js/inquiry/input01.js"></script>

</body>
</html>

画面の初期表示は以下のようになります。

f:id:ksby:20170729090551p:plain f:id:ksby:20170729091805p:plain

画面上の入力項目を全て入力し、入力チェックでエラーが出なかった場合は以下のようになります。

f:id:ksby:20170729090905p:plain

            <!-- 職業 -->
            <div class="form-group has-success" id="form-group-job">
              ..........
            </div>

            <div class="text-center">
              <button class="btn bg-green js-btn-next"><i class="fa fa-arrow-right"></i> 次へ</button>
            </div>
  • form-group に has-success を追加して <div class="form-group" ...><div class="form-group has-success" ...> にします。
  • disabled を削除して <button class="btn bg-green js-btn-next" disabled><button class="btn bg-green js-btn-next"> にします。

入力チェックがエラーだった場合は以下のようになります。

f:id:ksby:20170729092216p:plain

            <!-- 職業 -->
            <div class="form-group has-error" id="form-group-job">
              ..........
              <div class="col-sm-10">
                ..........
                <div class="row js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>
  • form-group に has-error を追加して <div class="form-group" ...><div class="form-group has-error" ...> にします。
  • エラーメッセージの表示部分から hidden を削除して <div class="row hidden js-errmsg"><div class="row js-errmsg"> にします。

今回 HTML を作成して思いましたが browser-sync が本当に便利です。手動でリロードをしなくてよいのがこんなに便利だとは。もっと早く知りたかった。。。

履歴

2017/07/29
初版発行。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その7 )( webpack + browser-sync をインストールする )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その6 )( URL の決定 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • 各画面の HTML を作成する前に、今回は HTML の確認を Tomcat ではなく webpack + browser-sync を使用して行ってみようと思います。
    • まずはインストールして試してみます。
  • 「Spring Boot + npm + Geb で入力フォームを作ってテストする」では webpack は js ファイルの開発にしか使わないので HTML の確認だけなら browser-sync だけでいいのですが、両方インストールして変更したファイル(html ファイルだけでなく js ファイルも)を watch する仕組みを構築します。

参照したサイト・書籍

  1. 最新版で学ぶwebpack 3入門 – JavaScript開発で人気のバンドルツール
    https://ics.media/entry/12140

  2. Building Static Sites with Webpack
    https://blog.binoy.io/building-static-sites-with-webpack-4fc489ceabca

  3. WebPack-Dev-ServerからBrowsersyncに乗り換えた
    https://saku.io/move-from-webpack-dev-server-to-browser-sync/

  4. webpackで複数のディレクトリへ出力する
    http://webdesign-dackel.com/2015/09/10/webpack-multiple-output/

  5. window.onloadとjQueryの$(document).ready等の比較
    https://rcmdnk.com/blog/2015/07/11/computer-javascript-jquery/

目次

  1. webpack をインストールする
  2. browser-sync をインストールする
  3. webpack + browser-sync 起動用の npm-scripts を作成する
  4. 動作確認する

手順

webpack をインストールする

npm install --save-dev webpack コマンドを実行して webpack をインストールします。

f:id:ksby:20170724061949p:plain f:id:ksby:20170724062146p:plain f:id:ksby:20170724062319p:plain f:id:ksby:20170724062428p:plain f:id:ksby:20170724062534p:plain

プロジェクトのルートディレクトリ直下に webpack.config.js というファイルを新規作成し、以下の内容を記述します。

module.exports = {
    entry: {
        "js/app": ["./src/main/assets/js/app.js"]
    },
    output: {
        path: __dirname + "/src/main/resources/static",
        publicPath: "/",
        filename: "[name].js"
    }
};
  • js ファイルは src/main の下に assets/js ディレクトリを作成し、その下に作成します。
  • 今回は動作確認のために app.js を作成します。
  • webpack により処理・生成された js ファイルは src/main/resources/static/js の下に配置します。

src/main の下に assets/js ディレクトリを作成し、src/main/assets/js の下に app.js を作成します。

f:id:ksby:20170724063549p:plain

app.js には以下の内容を記述します。

var $ = require("admin-lte/plugins/jQuery/jquery-2.2.3.min.js");

webpack を起動しやすくするために package.json の scripts に以下の記述を追加します。

  "scripts": {
    ..........,
    "webpack": "webpack"
  },

動作確認します。まずは npm run webpack -- --version コマンドを実行してバージョン番号が表示されることを確認します。

f:id:ksby:20170724064207p:plain

3.3.0 と表示されました。

次に npm run webpack コマンドを実行し、src/main/assets/js/app.js から src/main/resources/static/js/app.js が生成されることを確認します。

f:id:ksby:20170724064532p:plain

コマンドが正常に終了し、src/main/resources/static/js の下に app.js が生成されています。

f:id:ksby:20170724064722p:plain

app.js を開くと以下の内容で始まるファイルでした。

f:id:ksby:20170724064903p:plain

問題なく動作しているようですので、次へ進みます。

browser-sync をインストールする

npm install --save-dev browser-sync コマンドを実行して browser-sync をインストールします。

f:id:ksby:20170724065807p:plain f:id:ksby:20170724065903p:plain f:id:ksby:20170724070002p:plain f:id:ksby:20170724070103p:plain f:id:ksby:20170724070208p:plain f:id:ksby:20170724070311p:plain

package.json の scripts に以下の記述を追加します。

  "scripts": {
    ..........,
    "browser-sync": "browser-sync"
  },

npm run browser-sync -- init コマンドを実行して bs-config.js を生成します。

f:id:ksby:20170724070615p:plain

bs-config.js の以下の点を変更します。

  • "weinre": { "port": 8080 }"weinre": { "port": 9081 } に変更します。
  • "files": false"files": [ "./static/**/*", "./src/main/resources/static/**/*" ] に変更します。
  • "server": false"server": { "baseDir": [ "./static", "./src/main/resources/static" ] }に変更します。
  • "port": 3000"port": 9080 に変更します。
  • "notify": true"notify": false に変更します。
/*
 |--------------------------------------------------------------------------
 | Browser-sync config file
 |--------------------------------------------------------------------------
 |
 | For up-to-date information about the options:
 |   http://www.browsersync.io/docs/options/
 |
 | There are more options than you see here, these are just the ones that are
 | set internally. See the website for more info.
 |
 |
 */
module.exports = {
    "ui": {
        "port": 3001,
        "weinre": {
            "port": 9081
        }
    },
    "files": [
        "./static/**/*",
        "./src/main/resources/static/**/*"
    ],
    "watchEvents": [
        "change"
    ],
    "watchOptions": {
        "ignoreInitial": true
    },
    "server": {
        "baseDir": [
            "./static",
            "./src/main/resources/static"
        ]
    },
    "proxy": false,
    "port": 9080,
    "middleware": false,
    "serveStatic": [],
    "ghostMode": {
        "clicks": true,
        "scroll": true,
        "location": true,
        "forms": {
            "submit": true,
            "inputs": true,
            "toggles": true
        }
    },
    "logLevel": "info",
    "logPrefix": "Browsersync",
    "logConnections": false,
    "logFileChanges": true,
    "logSnippet": true,
    "rewriteRules": [],
    "open": "local",
    "browser": "default",
    "cors": false,
    "xip": false,
    "hostnameSuffix": false,
    "reloadOnRestart": false,
    "notify": false,
    "scrollProportionally": true,
    "scrollThrottle": 0,
    "scrollRestoreTechnique": "window.name",
    "scrollElements": [],
    "scrollElementMapping": [],
    "reloadDelay": 0,
    "reloadDebounce": 0,
    "reloadThrottle": 0,
    "plugins": [],
    "injectChanges": true,
    "startPath": null,
    "minify": true,
    "host": null,
    "localOnly": false,
    "codeSync": true,
    "timestamps": true,
    "clientEvents": [
        "scroll",
        "scroll:element",
        "input:text",
        "input:toggles",
        "form:submit",
        "form:reset",
        "click"
    ],
    "socket": {
        "socketIoOptions": {
            "log": false
        },
        "socketIoClientConfig": {
            "reconnectionAttempts": 50
        },
        "path": "/browser-sync/socket.io",
        "clientPath": "/browser-sync",
        "namespace": "/browser-sync",
        "clients": {
            "heartbeatTimeout": 5000
        }
    },
    "tagNames": {
        "less": "link",
        "scss": "link",
        "css": "link",
        "jpg": "img",
        "jpeg": "img",
        "png": "img",
        "svg": "img",
        "gif": "img",
        "js": "script"
    }
};

package.json の scripts に以下の記述を追加します。

  "scripts": {
    ..........,
    "browser-sync:start": "browser-sync start --config bs-config.js"
  },

プロジェクトのルートディレクトリ直下に static ディレクトリを作成します。今回は Thymeleaf テンプレート前の html ファイルは jar ファイルに入れないようにするために、src/main/resources/static ではなく static の下に作成することにします。

npm run browser-sync:start コマンドを実行してみます。

f:id:ksby:20170724072032p:plain

特にエラーは出ずに起動しているようです。http://localhost:3001 にアクセスすると browser-sync の ui が表示されました。

f:id:ksby:20170724072148p:plain

npm run browser-sync:start コマンドを実行したコマンドプロンプトで Ctrl+C を押して終了すると、ブラウザに以下の画面が表示されました。サーバと同期して動いているようです。

f:id:ksby:20170724072416p:plain

問題なさそうですので、次へ進みます。

webpack + browser-sync 起動用の npm-scripts を作成する

package.json の scripts に以下の記述を追加します。

  "scripts": {
    ..........,
    "webpack": "webpack",
    "webpack:watch": "webpack --watch",
    "browser-sync": "browser-sync",
    "browser-sync:start": "browser-sync start --config bs-config.js",
    "server": "run-p webpack:watch browser-sync:start"
  },
  • "webpack:watch": "webpack --watch" を追加します。
  • "server": "run-p webpack:watch browser-sync:start" を追加します。

npm run server コマンドで webpack と browser-sync が起動して、ファイルが変更されるとブラウザに自動反映されるようにします。

動作確認する

static の下に以下の内容の sample.html を作成します。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
テスト用ページです。<br/>
<span id="sample">xxx</span>
<script src="/js/app.js"></script>
</body>
</html>

src/main/assets/js/app.js を以下の内容に変更します。

var $ = require("admin-lte/plugins/jQuery/jquery-2.2.3.min.js");

$(document).ready(function() {
    $("#sample").text("サンプル");
});

npm run server コマンドを実行します。

f:id:ksby:20170724213650p:plain

ブラウザが起動しますので、http://localhost:9080/sample.html にアクセスしてみます。

f:id:ksby:20170724213858p:plain

sample.html に記述した文字列が表示されており、また app.js に記述した処理も反映されていました。

sample.html を以下の内容に変更してみます。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
サンプルですよ?<br/>
<span id="sample">xxx</span>
<script src="/js/app.js"></script>
</body>
</html>

ブラウザを見るとリロードすることなく反映されています。

f:id:ksby:20170724214205p:plain

今度は app.js を以下の内容に修正してみます。

var $ = require("admin-lte/plugins/jQuery/jquery-2.2.3.min.js");

$(document).ready(function() {
    $("#sample").text("app.js を修正してみました");
});

webpack の処理が実行されて src/main/resources/static/js/app.js が作り直されて、

f:id:ksby:20170724214540p:plain

ブラウザの方に手動でリロードすることなく反映されました。

f:id:ksby:20170724214748p:plain

他にも browser-sync はこんなことができるようです。

  • 複数のブラウザ(下の画像では IEChrome)で同じページを見ている時に、ファイルが変更されると全てのブラウザに手動リロードすることなく反映されます。

    f:id:ksby:20170724223414p:plain

  • 複数のブラウザで同じフォームを見ている時に、片方のブラウザ(IE)に入力すると、自動で別のブラウザ(Chrome)にも入力されます。

    f:id:ksby:20170724223724p:plain

  • sample2.html を作成します。その後で sample.html には sample.html → sample2.html へ遷移するリンクを、sample2.html には sample2.html → sample.html へ遷移するリンクを記述してから IEChrome で sample.html を表示した後

    f:id:ksby:20170724224224p:plain

    IE の方のリンクをクリックして sample2.html へ遷移すると、Chrome の方でも sample2.html へ遷移します。

    f:id:ksby:20170724224858p:plain

browser-sync ってすごい面白い! まさか、こんな便利なソフトがあるとは。

他にも browser-sync には proxy の機能もあるようで、Spring Boot の Tomcat と連携して自動リロードもできるのでしょうか。。。? 入力フォームができたら試してみたいと思います。

Ctrl+C を押して webpack と browser-sync を停止します。今回作成した sample.html, sample2.html は commit せずに削除します。

最後に package.json, webpack.config.js, bs-config.js を記載しておきます。

ソースコード

package.json

{
  "name": "boot-npm-geb-sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "postinstall": "run-s clean:static-dir copy:all",
    "clean:static-dir": "rimraf src/main/resources/static/*",
    "copy:all": "run-p copy:bootstrap copy:admin-lte copy:font-awesome copy:ionicons",
    "copy:bootstrap": "cpx node_modules/bootstrap/dist/**/* src/main/resources/static/vendor/bootstrap",
    "copy:admin-lte": "cpx node_modules/admin-lte/dist/**/* src/main/resources/static/vendor/admin-lte",
    "copy:font-awesome": "cpx node_modules/font-awesome/{css,fonts}/**/* src/main/resources/static/vendor/font-awesome",
    "copy:ionicons": "cpx node_modules/ionicons/dist/{css,fonts}/**/* src/main/resources/static/vendor/ionicons",
    "webpack": "webpack",
    "webpack:watch": "webpack --watch",
    "browser-sync": "browser-sync",
    "browser-sync:start": "browser-sync start --config bs-config.js",
    "server": "run-p webpack:watch browser-sync:start"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "admin-lte": "^2.3.11",
    "bootstrap": "^3.3.7",
    "font-awesome": "^4.7.0",
    "ionicons": "^3.0.0"
  },
  "devDependencies": {
    "browser-sync": "^2.18.13",
    "cpx": "^1.5.0",
    "npm-run-all": "^4.0.2",
    "rimraf": "^2.6.1",
    "webpack": "^3.3.0"
  }
}

webpack.config.js

module.exports = {
    entry: {
        "js/app": ["./src/main/assets/js/app.js"]
    },
    output: {
        path: __dirname + "/src/main/resources/static",
        publicPath: "/",
        filename: "[name].js"
    }
};

bs-config.js

/*
 |--------------------------------------------------------------------------
 | Browser-sync config file
 |--------------------------------------------------------------------------
 |
 | For up-to-date information about the options:
 |   http://www.browsersync.io/docs/options/
 |
 | There are more options than you see here, these are just the ones that are
 | set internally. See the website for more info.
 |
 |
 */
module.exports = {
    "ui": {
        "port": 3001,
        "weinre": {
            "port": 9081
        }
    },
    "files": [
        "./static/**/*",
        "./src/main/resources/static/**/*"
    ],
    "watchEvents": [
        "change"
    ],
    "watchOptions": {
        "ignoreInitial": true
    },
    "server": {
        "baseDir": [
            "./static",
            "./src/main/resources/static"
        ]
    },
    "proxy": false,
    "port": 9080,
    "middleware": false,
    "serveStatic": [],
    "ghostMode": {
        "clicks": true,
        "scroll": true,
        "location": true,
        "forms": {
            "submit": true,
            "inputs": true,
            "toggles": true
        }
    },
    "logLevel": "info",
    "logPrefix": "Browsersync",
    "logConnections": false,
    "logFileChanges": true,
    "logSnippet": true,
    "rewriteRules": [],
    "open": "local",
    "browser": "default",
    "cors": false,
    "xip": false,
    "hostnameSuffix": false,
    "reloadOnRestart": false,
    "notify": false,
    "scrollProportionally": true,
    "scrollThrottle": 0,
    "scrollRestoreTechnique": "window.name",
    "scrollElements": [],
    "scrollElementMapping": [],
    "reloadDelay": 0,
    "reloadDebounce": 0,
    "reloadThrottle": 0,
    "plugins": [],
    "injectChanges": true,
    "startPath": null,
    "minify": true,
    "host": null,
    "localOnly": false,
    "codeSync": true,
    "timestamps": true,
    "clientEvents": [
        "scroll",
        "scroll:element",
        "input:text",
        "input:toggles",
        "form:submit",
        "form:reset",
        "click"
    ],
    "socket": {
        "socketIoOptions": {
            "log": false
        },
        "socketIoClientConfig": {
            "reconnectionAttempts": 50
        },
        "path": "/browser-sync/socket.io",
        "clientPath": "/browser-sync",
        "namespace": "/browser-sync",
        "clients": {
            "heartbeatTimeout": 5000
        }
    },
    "tagNames": {
        "less": "link",
        "scss": "link",
        "css": "link",
        "jpg": "img",
        "jpeg": "img",
        "png": "img",
        "svg": "img",
        "gif": "img",
        "js": "script"
    }
};

履歴

2017/07/24
初版発行。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その6 )( URL の決定 )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その5 )( Bootstrap, AdminLTE, Font Awesome, Ionicons のインストール ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • 画面の URL を決めます。

参照したサイト・書籍

目次

  1. 画面の URL を決める

手順

画面の URL を決める

各画面の URL は以下のようにします。

画面 アクション URL
入力画面1 初期表示 /inquiry/input/01/
「次へ」ボタンクリック時 /inquiry/input/01/?move=next
入力画面2 初期表示 /inquiry/input/02/
「前の画面へ戻る」ボタンクリック時 /inquiry/input/02/?move=back
「次へ」ボタンクリック時 /inquiry/input/02/?move=next
入力画面3 初期表示 /inquiry/input/03/
「前の画面へ戻る」ボタンクリック時 /inquiry/input/03/?move=back
「確認画面へ」ボタンクリック時 /inquiry/input/03/?move=next
確認画面 初期表示 /inquiry/confirm/
入力画面1の項目の「修正する」ボタンクリック時 /inquiry/input/01/
入力画面2の項目の「修正する」ボタンクリック時 /inquiry/input/02/
入力画面3の項目の「修正する」ボタンクリック時 /inquiry/input/03/
「送信する」ボタンクリック時 /inquiry/confirm/send/
完了画面 初期表示 /inquiry/complete/

履歴

2017/07/23
初版発行。
2017/08/06
* 入力画面3のボタン名を「次へ」→「確認画面へ」に変更しました。
2017/08/08
* HTML を作成した時に「前の画面へ戻る」ボタンに記述した CSS のスタイルを js-btn-back にしていたので URL も move=prevmove=back へ変更しました。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その5 )( Bootstrap, AdminLTE, Font Awesome, Ionicons のインストール )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その4 )( nodist + Node.js のインストール ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • npm を使用して Bootstrap, AdminLTE, Font Awesome, Ionicons をインストールします。
    • インストール後 src/main/resources/static へ必要なファイルをコピーする npm-scripts も作成します。

参照したサイト・書籍

  1. Purpose of installing bootstrap through npm?
    https://stackoverflow.com/questions/26773767/purpose-of-installing-bootstrap-through-npm

  2. npm でパッケージのバージョン一覧を確認したりバージョンを指定してインストールしたりする方法
    http://phiary.me/npm-package-version-info-install/

  3. npm scriptsを使おう
    http://qiita.com/liply/items/cccc6a7b703c1d3ab04f

  4. npm-scripts で使える便利モジュールたち
    http://qiita.com/mysticatea/items/12bb6579b9155fd74586

  5. cpx と rimraf を試す
    http://akabeko.me/blog/2015/09/cpx-rimraf/

  6. cpx
    https://www.npmjs.com/package/cpx

  7. rimraf
    https://www.npmjs.com/package/rimraf

  8. npm-run-all
    https://www.npmjs.com/package/npm-run-all

  9. npm runコマンドを実行したら警告が出るようになった
    http://qiita.com/isamusuzuki/items/738a2736a746b67bd977

目次

  1. package.json を生成する
  2. Bootstrap をインストールする
  3. AdminLTE をインストールする
  4. Font Awesome をインストールする
  5. Ionicons をインストールする
  6. install 後に node_modules → src/main/resources/static へファイルをコピーする npm-scripts を作成する
  7. AdminLTE の starter.html で確認する
  8. .gitignore を変更する
  9. 最後に

手順

package.json を生成する

  1. npm init -y コマンドを実行して package.json を生成します。

    f:id:ksby:20170722145004p:plain

    今は生成されたままとし、特に変更はしません。

Bootstrap をインストールする

  1. npm info bootstrap versions コマンドを実行してインストール可能な Bootstrap のバージョン一覧を表示します。

    f:id:ksby:20170722150304p:plain

  2. npm install --save bootstrap コマンドを実行して Bootstrap をインストールします。bootstrap@3.3.7 と表示されており、3.3.7 がインストールされました。

    f:id:ksby:20170722150624p:plain

  3. インストール先の node_modules/bootstrap の下を見ると以下のディレクトリ構成になっていました。単に使用したいのであれば dist ディレクトリの下のファイルを使えば良さそうです。

    f:id:ksby:20170722151144p:plain

AdminLTE をインストールする

  1. Google で “npm AdminLTE” で検索すると https://www.npmjs.com/package/admin-lte のページがヒットしました。npm でインストールする時のパッケージ名は admin-lte のようです。

    npm install --save admin-lte コマンドを実行して Bootstrap をインストールします。

    f:id:ksby:20170722152034p:plain

  2. インストール先の node_modules/admin-lte の下を見ると以下のディレクトリ構成になっていました。こちらも単に使用したいのであれば dist ディレクトリの下のファイルを使えば良さそうです。

    f:id:ksby:20170722152414p:plain

    jQuery も node_modules/admin-lte/plugins/jQuery の下に jquery-2.2.3.min.js があります。今回はこのファイルを使用します。

    f:id:ksby:20170722152615p:plain

Font Awesome をインストールする

  1. “npm Font Awesome” で検索すると https://www.npmjs.com/package/font-awesome がヒットしました。

    npm install --save font-awesome コマンドを実行して Font Awesome をインストールします。

    f:id:ksby:20170722174852p:plain

  2. インストール先の node_modules/font-awesome の下を見ると以下のディレクトリ構成になっていました。単に使用したいのであれば css, fonts ディレクトリの下のファイルを使えば良さそうです。

    f:id:ksby:20170722175203p:plain

Ionicons をインストールする

  1. “npm Ionicons” で検索すると https://www.npmjs.com/package/ionicons がヒットしました。

    npm install --save ionicons コマンドを実行して Ionicons をインストールします。

    f:id:ksby:20170722175714p:plain

  2. インストール先の node_modules/font-awesome の下を見ると以下のディレクトリ構成になっていました。単に使用したいのであれば css, fonts ディレクトリの下のファイルを使えば良さそうです(dist に scss 用のファイルとかも入っていて分かりづらいです)。

    f:id:ksby:20170722180247p:plain

install 後に node_modules → src/main/resources/static へファイルをコピーする npm-scripts を作成する

以下のコマンドを実行して npm-scripts を作成する上で便利なパッケージをインストールします。

  • npm install --save-dev cpx
  • npm install --save-dev rimraf
  • npm install --save-dev npm-run-all

package.json の scripts にコピー用の script を定義します。パッケージのファイルは src/main/resources/static/vendor/<パッケージ名>/ の下にコピーします。

  "scripts": {
    ..........
    "postinstall": "run-s clean:static-dir copy:all",
    "clean:static-dir": "rimraf src/main/resources/static/*",
    "copy:all": "run-p copy:bootstrap copy:admin-lte copy:font-awesome copy:ionicons",
    "copy:bootstrap": "cpx node_modules/bootstrap/dist/**/* src/main/resources/static/vendor/bootstrap",
    "copy:admin-lte": "cpx node_modules/admin-lte/dist/**/* src/main/resources/static/vendor/admin-lte",
    "copy:font-awesome": "cpx node_modules/font-awesome/{css,fonts}/**/* src/main/resources/static/vendor/font-awesome",
    "copy:ionicons": "cpx node_modules/ionicons/dist/{css,fonts}/**/* src/main/resources/static/vendor/ionicons"
  },
  • postinstall を追加し、npm install コマンドが実行された後にファイルのコピーを行う処理を記述します。run-s コマンドで clean:static-dir → copy:all の順に script を実行します。
  • clean:static-dir は src/main/resources/static の下のディレクトリ、ファイルを削除します。static ディレクトリ自体は削除しません。
  • copy:all は node_modules の下から必要なファイルを src/main/resources/static の下にコピーします。以下の4つの script を run-p コマンドで並列実行します。
    • copy:bootstrap は node_modules/bootstrap パッケージの下のファイルをコピーします。
    • copy:admin-lte は node_modules/admin-lte パッケージの下のファイルをコピーします。
    • copy:font-awesome は node_modules/font-awesome パッケージの下のファイルをコピーします。
    • copy:ionicons は node_modules/ionicons パッケージの下のファイルをコピーします。

npm install コマンドを実行してみます。

f:id:ksby:20170723004623p:plain f:id:ksby:20170723004808p:plain

The node binary used for scripts is C:\Nodist\bin\node.exe but npm is using C:\Nodist\v-x64\6.11.1\node.exe itself. Use the–scripts-prepend-node-pathoption to include the path for the node binary npm was executed with. というメッセージが大量に出力されました(というより npm install コマンドでパッケージをインストールしている時から出ていて WARN だったので気にしていなかっただけでしたが)。さすがに量が多いので出力されないようにする方法があるか確認します。

Web で調べたところ npm runコマンドを実行したら警告が出るようになった に解決方法が記載されていました。npm config set scripts-prepend-node-path true コマンドを実行します。

再度 npm install コマンドを実行します。

f:id:ksby:20170723005544p:plain f:id:ksby:20170723005653p:plain

まだ npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.0.0 (node_modules\chokidar\node_modules\fsevents): というメッセージが出ます。

Web で調べたところ fsevents は Mac でしか使用できないパッケージのためメッセージが出力されていますが、WARN なので気にしなければよいようです。

src/main/resources/static の下を見ると以下の画像のようにディレクトリが作成されていました。

f:id:ksby:20170723011220p:plain

AdminLTE の starter.html で確認する

node_modules/admin-lte の下に starter.html がありますので、それを src/main/resources/static の下にコピーした後、以下の点を変更します。

  • <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"><link rel="stylesheet" href="/vendor/bootstrap/css/bootstrap.min.css">
  • <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css"><link rel="stylesheet" href="/vendor/font-awesome/css/font-awesome.min.css">
  • <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css"><link rel="stylesheet" href="/vendor/ionicons/css/ionicons.min.css">
  • <link rel="stylesheet" href="dist/css/AdminLTE.min.css"><link rel="stylesheet" href="/vendor/admin-lte/css/AdminLTE.min.css">
  • <link rel="stylesheet" href="dist/css/skins/skin-blue.min.css"><link rel="stylesheet" href="/vendor/admin-lte/css/skins/skin-blue.min.css">
  • 以下の2行はコメントアウトします。
    • <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
    • <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
  • body タグ内の <img src="dist/img/...<img src="/vendor/admin-lte/img/...

head タグ内は以下のようになります。

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>AdminLTE 2 | Starter</title>
  <!-- Tell the browser to be responsive to screen width -->
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
  <!-- Bootstrap 3.3.6 -->
  <link rel="stylesheet" href="/vendor/bootstrap/css/bootstrap.min.css">
  <!-- Font Awesome -->
  <link rel="stylesheet" href="/vendor/font-awesome/css/font-awesome.min.css">
  <!-- Ionicons -->
  <link rel="stylesheet" href="/vendor/ionicons/css/ionicons.min.css">
  <!-- Theme style -->
  <link rel="stylesheet" href="/vendor/admin-lte/css/AdminLTE.min.css">
  <!-- AdminLTE Skins. We have chosen the skin-blue for this starter
        page. However, you can choose any other skin. Make sure you
        apply the skin class to the body tag so the changes take effect.
  -->
  <link rel="stylesheet" href="/vendor/admin-lte/css/skins/skin-blue.min.css">

  <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
  <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
  <!--&lt;!&ndash;[if lt IE 9]>-->
  <!--<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>-->
  <!--<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>-->
  <!--<![endif]&ndash;&gt;-->
</head>

bootRun タスクを実行して Tomcat を起動した後、http://localhost:8080/starter.html にアクセスすると画面が表示されました。

f:id:ksby:20170723013450p:plain

Tomcat を停止します。

.gitignore を変更する

  1. commit する前に .gitignore を以下の方針で変更します。

    • node_modules は commit の対象にしません。
    • src/main/resources/static も commit の対象にしません。
    • npm 実行時にエラーが出ると出力される npm-debug.log も commit の対象にしません。

    .gitignore を リンク先の内容 に変更します。

    commit すると変更した .gitignore と、生成した package.json だけでした。npm install コマンドを実行すれば必要なファイルがインストール + コピーされます。

最後に

npm + npm-scripts が便利ですね。最初 Node.js + npm 関連を調べた時は最近の流行りは grunt, gulp ではなく webpack になったというような記事を見ていたので、npm でインストールした Bootstrap の css ファイル等を src/main/resources/static の下にコピーしたい場合にも webpack でやるのかな、と思って試行錯誤していたのですが(ファイルをコピーするための copy-webpack-plugin は見つけました)、単なるコピーは npm-scripts の方が楽でした。

現時点での感想として、

  • npm-scripts と webpack を必要に応じて使い分けるのが良さそう。
  • コピーするだけのような簡単な処理だったりコマンド実行するような場合には npm-scripts で。
  • 自分で js ファイルを実装して、実行用に1つにまとめるような場合には webpack で。
  • SPA で js ファイル以外に css や html のファイルも1つにまとめる場合も webpack で。
  • Gulpで始めるwebpack 3入門 という記事も見かけました。どうも大規模開発になったら webpack に gulp を組み合わせるといいらしいですが、今の自分ではその境地には至れず。。。 npm-scripts + webpack で進めて、必要性を感じたら gulp を使う流れかな? 今は無理に手を出さないことにします。

今回の作業で生成・変更した package.json の最終版を リンク先 に記載しておきます。

ソースコード

.gitignore

# built application files
*.apk
*.ap_

# files for the dex VM
*.dex

# Java class files
*.class

# generated files
**/bin/
**/gen/

# Local configuration file (sdk path, etc)
local.properties

# Eclipse project files
.classpath
.project

# Proguard folder generated by Eclipse
**/proguard/

# Intellij project files
*.iml
*.ipr
*.iws
**/.idea/
**/out/
**/src/main/resources/static

#Gradle
.gradletasknamecache
**/.gradle/
**/build/
**/bin/

#Node.js
**/node_modules/
npm-debug.log
  • **/src/main/resources/static を追加します。
  • #Node.js から下の行を追加します。

package.json

{
  "name": "boot-npm-geb-sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "postinstall": "run-s clean:static-dir copy:all",
    "clean:static-dir": "rimraf src/main/resources/static/*",
    "copy:all": "run-p copy:bootstrap copy:admin-lte copy:font-awesome copy:ionicons",
    "copy:bootstrap": "cpx node_modules/bootstrap/dist/**/* src/main/resources/static/vendor/bootstrap",
    "copy:admin-lte": "cpx node_modules/admin-lte/dist/**/* src/main/resources/static/vendor/admin-lte",
    "copy:font-awesome": "cpx node_modules/font-awesome/{css,fonts}/**/* src/main/resources/static/vendor/font-awesome",
    "copy:ionicons": "cpx node_modules/ionicons/dist/{css,fonts}/**/* src/main/resources/static/vendor/ionicons"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "admin-lte": "^2.3.11",
    "bootstrap": "^3.3.7",
    "font-awesome": "^4.7.0",
    "ionicons": "^3.0.0"
  },
  "devDependencies": {
    "cpx": "^1.5.0",
    "npm-run-all": "^4.0.2",
    "rimraf": "^2.6.1"
  }
}

履歴

2017/07/23
初版発行。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その4 )( nodist + Node.js のインストール )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その3 )( Project の作成2 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Node.js のインストール
    • Node.js は https://nodejs.org/ja/ からダウンロードできるインストーラーでインストールせず、Node.js のバージョン管理ツールである nodist を利用してインストールします。

参照したサイト・書籍

  1. Node.js
    https://nodejs.org/ja/

  2. nodist
    https://github.com/marcelklehr/nodist

  3. nodistでNode.jsをバージョン管理
    http://qiita.com/satoyan419/items/56e0b5f35912b9374305

  4. nodist 〜 Node.jsのバージョンを管理する for Windows
    http://tech.pjin.jp/blog/2016/12/12/how_to_use_nodist/

目次

  1. nodist をインストールする
  2. Node.js をインストールする

手順

nodist をインストールする

  1. https://github.com/marcelklehr/nodist/releases から NodistSetup-v0.8.8.exe をダウンロードします。

  2. NodistSetup-v0.8.8.exe を実行します。

  3. 「Welcome to Nodist Setup」画面が表示されます。「Next >」ボタンをクリックします。

  4. 「License Agreement」画面が表示されます。「I Agree」ボタンをクリックします。

  5. 「Choose Install Location」画面が表示されます。「Destination Folder」を C:\Nodist へ変更した後、「Install」ボタンをクリックします。

  6. インストールが実行されます。完了すると「Completing Nodist Setup」画面が表示されますので、「Finish」ボタンをクリックします。

  7. コマンドプロンプトを起動し、nodist -v コマンドを実行してバージョン番号が表示されることを確認します。

    f:id:ksby:20170722140816p:plain

Node.js をインストールする

  1. nodist dist コマンドを実行してインストール可能な Node.js のバージョン一覧を表示します。

    f:id:ksby:20170722141120p:plain f:id:ksby:20170722141310p:plain

    https://nodejs.org/ja/ を見ると現在の推奨版は 6.11.1 ですので、6.11.1 をインストールします。

    f:id:ksby:20170722141605p:plain

  2. 以下の画像のコマンドを実行して 6.11.1 をインストールした後、Node.js のカレントのバージョンを 6.11.1 へ切り替えます。

    f:id:ksby:20170722142120p:plain

  3. npm はアップデートしても 4.0.5 のままでした。

    f:id:ksby:20170722142440p:plain

履歴

2017/07/22
初版発行。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その3 )( Project の作成2 )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その2 )( Project の作成 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Project の作成
    • 前回長くなったので2回に分けています。今回は前回からの続きです。

参照したサイト・書籍

  1. DBCP - validationQuery for different Databases
    https://stackoverflow.com/questions/10684244/dbcp-validationquery-for-different-databases

    • H2 Database の validationQuery を調べた時に参照しました。H2 Database 以外の validationQuery も記載されています。ほとんどの DB で select 1 なんですね。

目次

  1. ksbysample-webapp-lending からファイルをコピーする(その2)
  2. 「Spring Configuration Check」のメッセージが表示されないようにする
  3. Tomcat が起動することを確認する

手順

ksbysample-webapp-lending からファイルをコピーする(その2)

  1. ksbysample-webapp-lending から以下のファイルをコピーします。

    ※リンクのあるファイルはリンク先の内容に変更します。

    ■src/main/resources

  2. ファイルコピー後は以下のディレクトリ構成になります。

    f:id:ksby:20170722071222p:plain f:id:ksby:20170722071357p:plain

「Spring Configuration Check」のメッセージが表示されないようにする

  1. IntelliJ IDEA を再起動します。

  2. 画面右下に「Spring Configuration Check」のメッセージが表示されます。

    f:id:ksby:20170722072847p:plain

    下矢印ボタンを押してメッセージの全てを表示した後、「boot-npm-geb-sample_main」リンクをクリックします。

    f:id:ksby:20170722073021p:plain

  3. 「Project Structure」ダイアログが表示されます。「+」ボタンをクリックして下に表示されているファイルを追加します。

    f:id:ksby:20170722073405p:plain

  4. 「New Application Context」ダイアログが表示されます。以下の画像のファイルをチェックした後、「OK」ボタンをクリックします。

    f:id:ksby:20170722073637p:plain

  5. 「Project Structure」ダイアログに戻ると以下の画像のように表示されます。下の画像では下半分を表示していませんが、まだこの段階では警告メッセージは消えていません。

    f:id:ksby:20170722073829p:plain

  6. 再び IntelliJ IDEA を再起動して、画面右下に「Spring Configuration Check」のメッセージが表示されないことを確認します。この後「Project Structure」ダイアログを表示すると警告メッセージは消えていました。

Tomcat が起動することを確認する

  1. Gradle Tool Window から bootRun タスクを実行します。Tomcat が起動して “Started Application in …” のログが出力されることを確認します。

    f:id:ksby:20170722075704p:plain

  2. Tomcat を停止します。

ソースコード

application.properties

doma.dialect=org.seasar.doma.jdbc.dialect.H2Dialect

spring.datasource.tomcat.url=jdbc:h2:mem:bootnpmgebdb
spring.datasource.tomcat.username=sa
spring.datasource.tomcat.password=
spring.datasource.tomcat.driverClassName=org.h2.Driver
spring.datasource.tomcat.initialSize=1
spring.datasource.tomcat.maxActive=1
spring.datasource.tomcat.maxIdle=1
spring.datasource.tomcat.minIdle=1
spring.datasource.tomcat.testOnBorrow=true
spring.datasource.tomcat.validationQuery=select 1
spring.datasource.tomcat.validationQueryTimeout=5
spring.datasource.tomcat.removeAbandoned=true
spring.datasource.tomcat.removeAbandonedTimeout=30
spring.datasource.tomcat.jdbc-interceptors=SlowQueryReport(threshold=5000)
# spring.datasource.jmx-enabled は spring.datasource.tomcat.jmx-enabled と書かないこと。
# spring.datasource.tomcat.jmx-enabled だと機能しない。
spring.datasource.jmx-enabled=true

spring.session.store-type=hash_map

spring.freemarker.cache=true
spring.freemarker.settings.number_format=computer
spring.freemarker.charset=UTF-8
spring.freemarker.enabled=false
spring.freemarker.prefer-file-system-access=false

spring.thymeleaf.mode=HTML

logging.level.root=INFO
logging.level.org.seasar.doma=ERROR

application-develop.properties

spring.mail.host=localhost
spring.mail.port=25

# Spring MVC
logging.level.org.springframework.web=DEBUG
# log4jdbc-log4j2
logging.level.jdbc.sqlonly=DEBUG
logging.level.jdbc.sqltiming=INFO
logging.level.jdbc.audit=INFO
logging.level.jdbc.resultset=ERROR
logging.level.jdbc.resultsettable=ERROR
logging.level.jdbc.connection=DEBUG

application-product.properties

server.tomcat.basedir=C:/webapps/boot-npm-geb-sample
logging.file=${server.tomcat.basedir}/logs/boot-npm-geb-sample.log
slowquery.logging.file=${server.tomcat.basedir}/logs/slow-query.log

spring.autoconfigure.exclude=com.integralblue.log4jdbc.spring.Log4jdbcAutoConfiguration
spring.datasource.tomcat.initialSize=1
spring.datasource.tomcat.maxActive=1
spring.datasource.tomcat.maxIdle=1
spring.datasource.tomcat.minIdle=1

spring.mail.host=localhost
spring.mail.port=25

application-unittest.properties

spring.autoconfigure.exclude=com.integralblue.log4jdbc.spring.Log4jdbcAutoConfiguration

spring.mail.host=localhost
spring.mail.port=25

logging.level.root=OFF

applicationContext-develop.xml

    <aop:config>
        <aop:pointcut id="pointcutService" expression="execution(* ksbysample.webapp.bootnpmgeb..*Service.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcutService"/>
    </aop:config>
  • execution(* ksbysample.webapp.lending..*Service.*(..))execution(* ksbysample.webapp.bootnpmgeb..*Service.*(..)) に変更します。

applicationContext-product.xml

    <aop:config>
        <aop:pointcut id="pointcutService" expression="execution(* ksbysample.webapp.bootnpmgeb..*Service.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcutService"/>
    </aop:config>
  • execution(* ksbysample.webapp.lending..*Service.*(..))execution(* ksbysample.webapp.bootnpmgeb..*Service.*(..)) に変更します。

applicationContext-unittest.xml

    <aop:config>
        <aop:pointcut id="pointcutService" expression="execution(* ksbysample.webapp.bootnpmgeb..*Service.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcutService"/>
    </aop:config>
  • execution(* ksbysample.webapp.lending..*Service.*(..))execution(* ksbysample.webapp.bootnpmgeb..*Service.*(..)) に変更します。

messages_ja_JP.properties

  • 中は空にします。

履歴

2017/07/22
初版発行。