かんがるーさんの日記

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

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
初版発行。