開発者ブログ

04.ビュー

Last modified by kashi on 2014/04/15, 16:45

概要

JavaScriptでは処理のどこからでもビュー操作(画面操作、DOM操作 とも言います)を行うことができます。
これは柔軟に機能を記述できる反面、どこでどんな処理をしているかがわかりにくくなり、保守フェーズにおける、ちょっとした画面の変更を困難にしがちです。

hifiveでは「ビュー操作は原則としてコントローラ内にのみ記述すること」と定めています。
これを守ることで「ビュー操作を行っているのは必ずコントローラである」ことが明らかになり、
保守・引継時にどこに手を付ければよいかがわかりやすくなります。

また、hifiveでは、ビュー生成のためにEJSというテンプレートエンジンを導入しています。
テンプレートエンジンを利用することで、複雑な構造を持つタグを視覚的に編集することができるため、
保守性や可読性の向上が期待できます。

単一要素に対する操作

<input>タグにセットされた値を読み取りたい・<div>タグの中に文字列を流し込みたい、など、
単一の要素の読み書きを行いたい場合はjQueryを使用します。

ただし、DOM要素を選択するとき、jQueryの「$()」をそのまま使用するのではなく、コントローラ内で使える
this.$find()メソッドを使用してください。
このメソッドはコントローラがバインドされた要素配下から要素を検索するので、
コントローラ(がバインドされた要素)の範囲外の要素を誤って操作することを防ぎます。

コントローラがバインドされている要素自身を対象にしたい場合は?

コントローラのインスタンスにはrootElementというプロパティがあり、
これを使うことでバインドされている要素を参照できます。
$(this.rootElement) とすることで、バインドされている要素自身をjQueryで操作できます。

操作例

※this.$find()の戻り値は「セレクタで指定された要素が選択された状態のjQueryオブジェクト」なので、
選択した後は通常のjQueryと同様に要素を操作することができます。

操作内容実装方法
要素生成$(タグ)
要素選択this.$find(セレクタ);
要素削除this.$find(セレクタ).remove();
要素の内容の更新this.$find(セレクタ).html(element);
this.$find(セレクタ).text(value);
this.$find(セレクタ).val(value);

※thisはコントローラ自身を指します。

複雑なビューの生成(テンプレートの使用)

基本構文

複数のタグで構成される複雑なビューを作成する場合は、ビューテンプレートを使用します。
1つのテンプレートは<script type="text/ejs"></script>で囲んだ中に記述します。
各テンプレートには一意なIDをつける必要があります(<script>タグのid属性としてセットします)。

EJSの構文は、JSPに非常によく似ています。
JavaScriptコードの開始は[%で、終了は%]で表します。[% %]は任意の場所に書くことができます。
中にはJavaScriptコードを記述することができるので、forループやif分岐などを使用して表示をカスタマイズできます。

<script type="text/ejs" id="list">
  [% for (var i = 1, len = num + 1; i < len; i++) { %]
    <li>
      <span style="[%= 'color: ' + (i % 2 === 0 ? 'blue;': 'red;') %]">[%= i %]</span>
    </li>
  [% } %]
</script>

テンプレートの定義場所

テンプレートは以下の2箇所で定義できます。

  1. 独立したファイルに記述
  2. HTML内に直接記述

基本的には独立したファイルに記述することをお勧めしますが、

  • そのテンプレートを特定の画面(HTML)でしか使用しない
  • HTTPリクエストの回数を最小限にしたい
    • 独立したファイルにした場合ファイルごとにHTTPリクエストが発生します

などの場合には、HTML内に直接記述する方式を検討するのもよいでしょう。

なお、テンプレートを独立したファイルにする場合、拡張子は「.ejs」にすることをお勧めします。

テンプレートファイルのロード

var sampleController = {  
  
  __templates: ['view/template1.ejs', 'view/template2.ejs'],  
  
}; 
  • テンプレートを外部ファイルとして定義した場合、それを使用するコントローラの__templatesプロパティで
    テンプレートファイルのURLを書いておくと、コントローラ化を行うときに自動的に読み込まれます。
    コントローラは指定されたテンプレートファイルが読み込まれた後に動作を開始するので、
    イベントハンドラなどでテンプレートを使用するときには必ず利用可能です。
  • HTMLに直接記述した場合は、いつでも使用することができます(__templatesプロパティを書く必要はありません)。

構文チェック用デバッグライブラリの導入

記述に誤りがあるテンプレートをロードした場合、通常テンプレートのどの箇所でエラーが起きているのかエラー内容から把握できません。
そこで、 エラー発生時に詳細なエラー内容とエラー行数を出力するライブラリがEJSには用意されています。
以下のサイトからダウンロードして下さい。
    http://embeddedjavascript.googlecode.com/files/ejs_fulljslint.js
使用方法については、以下のサイトを参照下さい。
    EJS - google code
    EJS公式HP

テンプレートに対する操作

テンプレートを使用し画面に反映させるときは、コントローラ内でthis.view以下の次のようなメソッドを使用します。
※thisはコントローラ自身を指します。
(下記のメソッドはコントローラ内で使用可能です。)

メソッド名説明
update(対象要素のオブジェクトまたはセレクタ, テンプレートID, パラメータ)対象要素の中身を、指定されたテンプレートで更新する。
append(対象要素のオブジェクトまたはセレクタ, テンプレートID, パラメータ)対象要素の末尾に、指定されたテンプレートを挿入する。
prepend(対象要素のオブジェクトまたはセレクタ, テンプレートID, パラメータ)対象要素の先頭に、指定されたテンプレートを挿入する。

直接画面に反映させずテンプレートを適用したタグ文字列を(コードで)受け取りたい場合は、
this.view.get(テンプレートID, 置換パラメータ)を使用してください。

実装例

1.HTMLを用意します。

<!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
    <script src="jquery.js"></script>
     <script src="ejs-1.0.h5mod.js"></script>
    <script src="h5.js"></script>
    <!-- ここで作成したjsファイルを読み込む -->
    <script src="step5.js"></script>
     <title>hifive ビュー操作</title>
  </head>
  <body>
    <div id="container">
      <div>
        1から入力した数値までを奇数は<span style="color: red;">赤色</span>で、
        偶数は<span style="color: blue;">青色</span>で画面に出力します。
      </div>
      <input type="text" id="to" value="" />
      <input type="button" id="output" value="出力" />
      <ul id="list"></ul> <!-- ここにテンプレート出力が流し込まれる -->
    </div>
  </body>
</html>

2.list.ejsファイルを作成します。

<script type="text/ejs" id="list">
[% for (var i = 1, len = num + 1; i < len; i++) { %]
<li>
  <span style="[%= 'color: ' + (i % 2 === 0 ? 'blue;': 'red;') %]">[%= i %]</span>
</li>
[% } %]
</script>

※<script>タグのidで指定した「list」が、this.view.get()で呼び出すときに必要なテンプレートIDとなります。

3.step5.jsファイルを作成します。

$(function() {
 // コントローラの元となるオブジェクトを作成
 var controller = {

 // コントローラ名を設定
 __name: 'NumListController',

 // 使用するテンプレートのパスを書く
 __templates: 'list.ejs',  // 複数ejsファイルを呼び出す場合は、配列で指定してください。

 // id=outputが押された時のイベントハンドラ
 '#output click': function() {
   // 入力値を取得
   var to = this.$find('#to').val();
   //値がなければ処理を終了
   if (!to) {
     return;
    }
   // StringからNumberへ変換。変換に失敗したら終了
   try {
      to = parseInt(to);
    } catch(e) {
     return;
    }

   // 値をバインドしたテンプレートを画面に出力する
     this.view.append('#list', 'list', {
        num: to
      });
    }
  };

 // id="container"である要素にコントローラをバインド
 h5.core.controller('#container', controller);
});

4.HTMLをブラウザで表示させます。

5.テキストボックスに数値を入力し出力ボタンをクリックします。

動作確認

チュートリアル/step5

テンプレートへ値を渡す方法

hifiveが提供するテンプレートはJSPと同じように、テンプレートの[% %]または[%= %]の位置に任意の値を設定することができます。[%= %]内の文字列は自動でHTMLエスケープされます。[%:= %]と書くとエスケープされません。
テンプレートへ渡す値は、以下のように指定します。

{テンプレートで使用する変数名: 変数が保持する値}

使用例(1)

this.view.append('#container', 'templateId1', {ar: [10, 20, 30]});

使用例(2)

複数の値を渡したい場合の記述方法は、オブジェクトリテラルと同じく、

{テンプレートで使用する変数名: 変数が保持する値, テンプレートで使用する変数名: 変数が保持する値, ...}

上記のようにカンマ区切りで指定します。

this.view.append('#container', 'templateId1', {ar: [10, 20, 30], str:'Hello World!'});

実装例では、step5.js の27行目、

this.view.append('#list', 'list', {num: to});

の第3引数{num: to}でテンプレートへ値を渡しており、渡した値は list.ejs の2行目

[% for (var i = 1, len = num + 1; i < len; i++) { %]

numで使用しています。

このように、オブジェクトのキー名をテンプレートで使用する変数名と同じ名前に指定することで、view.get()、view.append()、view.update()、 view.prepend()から、値を渡すことができます。

テンプレートの複数記述

外部ファイル・HTML直接定義どちらの場合も、複数のテンプレートを定義することができます。ただし、idは異なるものにしてください。

<script type="text/ejs" id="list1">
  <div>テスト1</div>
</script>
<script type="text/ejs" id="list2">
  <span>テスト2</span>
</script>

同じテンプレートIDを持つテンプレートが読み込まれたときの挙動

idが重複した場合、後に読み込まれたものが優先します(上書きされる)。
上書きされたくない場合は、同一ファイルまたは別ファイル関係になく、idは一意のものを指定して下さい。

ejsファイルの読み込み優先順は以下の通りです。

  1. ejsファイル内では上から下に順番に読み込まれるので、ファイルの後ろに書かれた定義が優先
  2. コントローラの__templatesプロパティに複数のejsファイルを指定した場合、配列の後ろに指定したejsファイルが優先

たとえば以下のようなコードの場合、実行すると「hello 2」が表示されます。

view.ejs

<script type="text/ejs" id="list">
  hello 1
</script>
<script type="text/ejs" id="list">
  hello 2
</script>

test.js

var testController = {
  __name: 'TestController',
  __templates: 'view.ejs',
  __ready: function() {
    alert(this.view.get('list'));
  }
}

h5.core.controller('body', testController);

test.html

<!doctype html>
<html>
 <head>
   <meta charset="UTF-8">
   <script src="jquery.js"></script>
   <script src="jquery.blockUI.js"></script>
   <script src="ejs-1.0.h5mod.js"></script>
   <script src="h5.js"></script>
   <script src="test.js"></script>
 </head>
 <body>
 </body>
</html>

ビュー操作に関する注意(idの一意性)

要素に設定するidは画面内で一意になるようにしてください。
これはHTMLの仕様です。

  • リスト行のように複数回画面に登場する場合、idでなくCSSクラスを割り当てるdata-*属性で一意なIDを割り当てる、等の方法を検討してください。
  • Ajax等で動的に画面書き換えを行う場合には特に注意が必要です。
  • 同じidを持つ要素が複数ある場合、this.$find('#id') や document.getElementById('id') などで要素を取得すると、多くのブラウザはページ内で最初に定義されている要素を取得します。ただし、動作は処理系に依存するので必ずしも同じ動作になるとは限りません。

ビュー(テンプレート)とコントローラ(イベント処理)を分離するだけで、コードはずいぶんとすっきりします。
クライアントでの計算コードがあまりない場合は、この2つだけを使うのもアリでしょう。
次は、計算・通信処理を行う「ロジック」について説明します。

次のステップ ⇒ 05.ロジック

参照


Copyright (C) 2012-2017 NS Solutions Corporation, All Rights Reserved.