8. 付録

この章では構成ライブラリを使用する際のTipsや構成ライブラリ以外の便利なライブラリの使用方法を説明する。

8.1. 構成ライブラリを使用する際のTips

8.1.1. BootstrapとSlickGridを同時に使用する際の注意点

ここでは、BootstrapとSlickGridを同時に使用する際の注意点を説明する。

8.1.1.1. スタイル競合によるテーブルのレイアウト崩れ

スタイル競合によりテーブルのレイアウト崩れ。

不具合事象サンプル

ヘッダとデータ行がずれる例

図: ヘッダとデータ行がずれる例

SlickGridでは、box-sizingプロパティの値がcontent-boxであることを前提にセルの幅と高さを計算してレイアウトを生成するが、Bootstrapはすべての要素に対してbox-sizingプロパティの値にborder-boxを設定する。その結果、SlickGridとBootstrapの併用時にテーブルのレイアウト崩れが発生する。

Note

border-boxは、髙さと幅のプロパティであるwidthheightに線の幅とパディングを含める設定(widthheightが画面上の表示サイズと等しい)である。一方でcontent-boxは線の幅とパディングを含めない設定(widthheightに線の幅とパディングを含めたものが画面上の表示サイズと等しい)である。

この事象に対して、ヘッダーとデータ行のセルのbox-sizingプロパティの値をcontent-boxにすることで対処する。 具体的には、ヘッダーのセルにSlickGridによって設定される.slick-header-column.ui-state-defaultとデータ行のセルに設定される.slick-cellに値を設定する。

.slick-header-column.ui-state-default {
  box-sizing: content-box;
}

.slick-cell {
  box-sizing: content-box;
}

対処後は崩れが解消される。

対処済みサンプル

対処後の例

図: 対処後の例

上記では、ライブラリのスタイルシート、JavaScriptへの変更を行わずに独自に作成したスタイルシートで対処を実施しているが、他にも方法が議論されているため、必要に応じて参照すること。

8.1.1.2. タブ内へのテーブル表示の際のレイアウト崩れ

初期状態で選択されていないタブのペイン内にテーブルを表示する場合、タブを切り替えてテーブルを表示すると右側のカラムが途中で欠ける場合がある。

これは、タブのペインがdisplay: noneで非表示となっていることで、SligkGridがレイアウトする際に幅を計算できないことが原因である。

公式ページでもdisplay: noneでは正確に計算できないと記載がある。

不具合事象サンプル

タブのペイン内にテーブル表示時、右側のカラムが欠ける例

図: タブのペイン内にテーブル表示時、右側のカラムが欠ける例

この事象に対して、初期表示時にテーブルを生成するのではなく、タブ切り替え時に初めてテーブルを作成するようにすることで対処する。 具体的には、Bootstrapのnav-tabタブクリック時のイベントである、shown.bs.tabを契機にテーブルを作成する処理を実装する。

SlickGrid基本構成サンプルのdefault.jsで実装している画面初期化処理を次のように実装する。

$('#tab2btn').one('shown.bs.tab', function () {
  new Slick.Grid('#myGrid', data, columns, options);
});

対処後は正常に表示される。

対処済みサンプル

対処後の例

図: 対処後の例

8.1.2. BootstrapとjQuery UIを同時に使用する際の名前空間の競合

BootstrapとjQuery UIを同時使用する際に、名前空間が競合することにより正常に動作がしない場合ある。

例えば、次のサンプルではモードレスダイアログの×ボタンが正常に表示されない。

不具合事象サンプル

モードレスダイアログの×ボタンが正常に表示されない例

図: モードレスダイアログの×ボタンが正常に表示されない例

これはjQuery UIがモーダルダイアログ作成の際に内部で実行する$.fn.button メソッドがBootstrapのメソッドと競合・上書きされているため、実行できないことが原因である。

この事象に対して、Bootstrapのメソッド毎に用意されているnoConflictメソッドを使用することで対処する。このメソッドを使用することで、上書きされる前のメソッドに差し戻すことができる。このサンプルでは$.fn.buttonが競合しているため以下のように$.fn.button.noConflictを使用する。

$.fn.bsbutton = $.fn.button.noConflict();

対処後は×ボタンが正常に表示されるようになる。

対処済みサンプル

対処後の例

図: 対処後の例

Note

jQueryでは$変数への競合のみ対処方法が用意されているが、Bootstrapのように個別のメソッドへの対処方法は用意されていない。そのため、BootstrapとjQuery UIを同時に使用する際は、基本的にBootstrapで名前空間の競合の対処を実施する。

8.2. 構成ライブラリ以外の便利なライブラリ

8.2.1. Lodashによるコーディング支援

Lo-Dashは、リスト操作やテンプレートエンジンなどの様々な便利な関数を提供するライブラリである。

ここではLo-Dashを用いた以下の内容を説明する。

利用ライブラリ 参考バージョン サンプル 参考ページ
Lo-Dash (Compatibility build) 4.17.10 なし Lo-Dash

8.2.1.1. リスト操作

ここでは代表的なメソッドのみ紹介する。 完全な一覧は Lodashの公式リファレンス を参照すること。

  • _.each(collection, callback)

    リストのすべての要素に対して繰り返し処理する。(Array.forEach相当)

    // (1)
    _([1, 2, 3]).forEach(function(num) {
      console.log(num);
    });
    
    項番 説明
    (1)
    リストの要素を1つずつコンソール出力する。
    上記サンプルの場合、コンソールにリストの要素を1つずつ順に出力する。
  • _.filter(collection, fn)

    リストの要素のうち、条件に一致した値だけのリストを返す。(Array.filter相当)

    // (2)
    _.filter([1, 2, 3, 4, 5, 6], function(num) {
      return num % 2 == 0;
    });
    
    項番 説明
    (2)
    2の倍数の要素からなるリストを返す。
    上記サンプルでは[2, 4, 6]が返却される。
  • _.filter(collection, props)

    指定したプロパティに一致する要素からなるリストを返す。

    var characters = [
      { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] },
      { 'name': 'fred',   'age': 40, 'pets': ['baby puss', 'dino'] }
    ];
    
    // (3)
    _.filter(characters, { 'age': 36 });
    
    項番 説明
    (3)
    ageプロパティが36である要素のリストを返す。
    上記サンプルでは[{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }]が返却される。
  • _.map(collection, callback)

    リスト要素のそれぞれに関数を適用した結果の新たなリストを返す。(Array.map相当)

    // (4)
    _.map([1, 2, 3], function(num) {
      return num * 3;
    });
    
    項番 説明
    (4)
    各要素の値を3倍したリストを作る。
    上記サンプルでは[3, 6, 9]が返却される。

8.2.1.2. テンプレートエンジン

_.template([string=''], [options={}])メソッドを用いることで、予め定義したテンプレート文字列と与えられたデータから、新たな文字列を生成することができる。 optionsではテンプレート記号の変更やインポートするオブジェクトの設定を行うことができる。optionsの詳細は Lodashの公式リファレンス を参照すること。

// (1)
var compiled = _.template('hello <%= name %>');
compiled({ 'name': 'fred' });

// (2)
var compiled = _.template('<b><%- value %></b>');
compiled({ 'value': '<script>' });

// (3)
var compiled = _.template('<% _.forEach(people, function(name) { %><li><%- name %></li><% }); %>');
compiled({ 'people': ['fred', 'barney'] });
項番 説明
(1)
プロパティの値で置き換える。(<%= ... %>を使用)
この場合、文字列hello fredが設定される。
(2)
プロパティの値をHTMLエスケープして置き換える。(<%- ... %>を使用)
この場合、文字列<b>&lt;script&gt;</b>が設定される。
(3)
テンプレート内でJavaScriptコードを実行する。(<% ... %>を使用)
この場合、文字列<li>fred</li><li>barney</li>が設定される。

Note

XSS脆弱性を作らないよう、HTML上のテキスト出力には<%- ... %>プレースホルダを使用すること。

Note

JSPなど、他のテンプレートエンジンが用いるテンプレート記号との競合が起きる可能性がある。 その場合は、Lodashが用いるテンプレート記号を次のようにして変更することができる。

_.templateSettings.escape      = /<@-([\s\S]+?)@>/g; // (1)
_.templateSettings.interpolate = /<@=([\s\S]+?)@>/g; // (2)
_.templateSettings.evaluate    = /<@([\s\S]+?)@>/g;  // (3)
項番 説明
(1)
<%- ... %><@- ... @>に変更する。
(2)
<%= ... %><@= ... @>に変更する。
(3)
<% ... %><@ ... @>に変更する。

8.3. Macchinetta Server Framework (1.x)との連携

8.3.1. Ajaxを利用した連携

8.3.1.1. 概要

ここでは、jQueryのajaxとMacchinetta Server Framework (1.x)を連携させる方法を紹介する。
クライアント側の実装に絞って説明するが、サーバ側の設定や実装も必要になる。詳細は Macchinettaオンライン版 開発ガイドライン ( https://macchinetta.github.io/server-guideline/current/ja/の Macchinetta Server Framework (1.x) Development Guideline ) の「Ajax」節と「CSRF対策」節を参照すること。
利用ライブラリ サンプル 参考ページ
jQuery - Ajax | jQuery

8.3.1.2. 利用方法

8.3.1.2.1. GETを使ったサーバ連携
以下のサンプルは、入力欄に数値を入力すると、非同期でサーバ側にデータを送信し、結果を「Result」または「Error」に反映する。
Ajaxを利用したサーバ連携のサンプル

図: Ajaxを利用したサーバ連携のサンプル

HTML(JSP)は以下のように実装する。
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <!-- (1) -->
    <meta name="contextPath" content="${pageContext.request.contextPath}" />
    <title>Ajax</title>
    <link rel="stylesheet" href="vendor/jquery-ui/jquery-ui.css">
  </head>
  <body>
    <h1>Ajax</h1>
    <form id="ajaxForm">
        <p>In this sample, it adds the input value, and then output to the 'Result' area.<br />
            If an error occurs, it outputs the cause to 'ERROR' area.</p>
        <label>Addition:</label>
        <!-- (2) -->
        <input type="text" name="num1" id="num1" value="100" />
        <input type="text" name="num2" id="num2" value="200" />
        <!-- (3) -->
        <br /><label>Result: <span id="result"></span></label>
        <!-- (4) -->
        <br /><label>Error: <span id="error"></span></label>
    </form>
    <!-- JavaScript -->
    <script src="${pageContext.request.contextPath}/resources/app/vendor/jquery/jquery.min.js"></script>
    <script src="${pageContext.request.contextPath}/resources/app/vendor/jquery-ui/jquery-ui.min.js"></script>
    <script src="${pageContext.request.contextPath}/resources/app/js/ajax.js"></script>
  </body>
</html>
項番 説明
(1)
JavaScriptからWebアプリケーションのコンテキストパスを取得できるよう、HTML(JSP)のmeta要素に設定にする。
(2)
値の入力欄を配置する。
(3)
計算結果の出力欄を設置する。
(4)
エラーの出力欄を設置する。
JavaScriptは以下のように実装する。
'use strict';

$(function () {

  // (1)
  var contextPath = $("meta[name='contextPath']").attr("content");
  var result = $('#result');
  var error = $('#error');

  // (2)
  $('#num1,#num2').on('change', function () {
    result.html('');
    error.html('');

    // (3)
    $.ajax({
      url: contextPath + '/api/v1/dummyCalc',
      type : 'GET',
      dataType : 'json',

      // (4)
      data : $('#ajaxForm').serialize(),
      timeout : 5000

    // (5)
    }).then(function(data) {
      result.html(data.result);
      return false;

    // (6)
    }).catch(function(jqXHR, textStatus, errorThrown) {
      if (textStatus === 'timeout') {
        error.html(errorThrown);
      }
      if (jqXHR.status === 400) {
        error.html(errorThrown);
      }
      return false;
    });
  });
});
項番 説明
(1)
HTML(JSP)のmeta要素に設定したコンテキストパスを取得する。
(2)
onchangeイベントの発生時に$.ajaxを実行する。
(3)
$.ajaxメソッドの本体。
リクエスト先のURLやHTTPのメソッドを指定し、Ajax通信を実行する。timeoutオプションは、指定した時間(ミリ秒)経過時にAjax通信が未完了の場合、タイムアウトを発生させる。

Note

$.ajaxメソッドはThe jQuery XMLHttpRequest (以下、jqXHRとする)オブジェクトを返却する。jqXHR オブジェクトはPromiseインターフェースを実装しているため、then/catchメソッドが使用できる。Promise(とそのスーパーセットであるDeferred)については Deferredによる非同期処理制御jQuery公式ウェブサイトのリファレンスを参照すること。

(4)
$.serializeメソッドを実行し、フォームデータをURLエンコードする。
(5)
Ajax通信が正常終了した時の処理を実装する。
ここでは、以下のJSON形式のレスポンスを受信することを想定している。
{"result":500}

Note

正常または異常終了はHTTPステータスコードで判断している。200番台と304番が正常終了と判断される。

(6)
Ajax通信が異常終了した時の処理を実装する。
タイムアウトの場合、textStatusに「timeout」が設定される。
HTTPステータスコードはjqXHR.statusから取得できる。

Note

【クロスドメインのscript取得について】

$.ajaxで他のドメインからscriptを取得する場合、dataType: 'script'の指定が 必須 となるため注意すること。

8.3.1.2.2. POSTを使ったサーバ連携
GETを使ったサーバ連携ではGETメソッドを使用した。
POSTメソッドも利用できるが、サーバ側のCSRF対策に応じた実装が必要になる。ここでは、POSTメソッドを利用する場合の実装を紹介する。
HTML(JSP)は、前述のサンプルに以下を追加する。
<!-- (1) -->
<sec:csrfMetaTags />
項番 説明
(1)
JavaScriptからCSRFトークンを取得できるよう、head要素にSpring Securityの<sec:csrfMetaTags>要素を実装する。なお、同要素はHTMLに以下のmeta要素を出力する。
<meta name="_csrf_parameter" content="_csrf" />
<meta name="_csrf_header" content="X-CSRF-TOKEN" />
<meta name="_csrf" content="0250c860-05d6-4a69-8dca-3d92681a2ee8" />
JavaScriptは前述のサンプルに以下を追加する。
var csrfToken = $("meta[name='_csrf']").attr("content");
var csrfHeaderName = $("meta[name='_csrf_header']").attr("content");

// (1)
$(document).ajaxSend(function(event, jqXHR, options) {
  jqXHR.setRequestHeader(csrfHeaderName, csrfToken);
});
また、ajaxのオプションを以下に変更する。
// (2)
type : 'POST'
項番 説明
(1)
ajax実行時に関数を実行し、HTTPリクエストヘッダにCSRFトークンを設定する。
(2)
$.ajaxtypeオプションをPOSTに修正する。