Do You PHP はてな このページをアンテナに追加 RSSフィード Twitter

2015-04-10

[]JsViewsチュートリアル - データリンクしたオブジェクトメソッドを呼び出す

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsRender入門 - Do You PHP はてなも参照してください。

今回やること

JsViewsでデータリンクしたオブジェクトメソッドを呼び出す。この際、データリンクした値が変更された場合にメソッド戻り値を再評価させてみる。

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result"></div>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// リンクされたオブジェクトのメソッドを呼び出す
$(function() {
    var data = {
        a: 'foo',
        b: 'bar',
        unlinkedFunction: function() {
            return 'hoge';
        },
        linkedFunction: function(a, b) {
            return a === b;
        },
        notLinkedFunction: function() {
            return this.a === this.b;
        },
        notLinkedFunctionWithDepends: function() {
            return this.a === this.b;
        }
    };

    // notLinkedFunctionWithDependsの戻り値はデータリンクした値に
    // 依存しているので、依存関係を定義する
    //
    //   [対象のメソッド].depends = [対象の変数, 依存するプロパティへのパス, ...]
    //
    // "*"は、dataオブジェクト直下のすべてのプロパティについて変化した
    // かどうかを監視する
    data.notLinkedFunctionWithDepends.depends = [data, '*'];

    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"と変数dataをリンク
    $.link.linkTemplate("#result", data);
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
<p>テキストボックスが同じ値の場合、boolの値が変化します</p>
<div>
    <p data-link="a"></p>
    <input type="text" data-link="a trigger=true"/>
</div>
<div>
    <p data-link="b"></p>
    <input type="text" data-link="b trigger=true"/>
</div>
<p>固定値:<span data-link="unlinkedFunction()"></span></p>
<p>linkedFunction(a, b):<span data-link="linkedFunction(a, b)"></span></p>
<p>notLinkedFunction():<span data-link="notLinkedFunction()"></span></p>
<p>notLinkedFunctionWithDespanends():<span data-link="notLinkedFunctionWithDepends()"></span></p>
</script>
</body>
</html>

説明

JsRenderチュートリアル - テンプレートに渡したオブジェクトのメソッドを呼び出す - Do You PHP はてなテンプレートに渡されたオブジェクトメソッドを呼び出してみましたが、JsViewsの場合も基本的にはそのまま記述すればOKです。ただし、データリンクした値が変更された場合にメソッド戻り値を再評価したいかどうかJavaScript側のコードが変わってきます。

1. 戻り値を再評価しなくて良い場合

JsRenderと同様の記述方法でOKです。unlinkedFunctionメソッドはデータリンクした値と関連がありません。

<span data-link="unlinkedFunction()"></span>
2. 戻り値を再評価するが、引数でデータリンクした値を受け取っている場合

これもJsRenderと同様の記述方法でOKです。linkedFunctionメソッドはデータリンクした値を受け取っているので、「データリンクした値が変更された場合にメソッドを呼び出す」といった依存関係が自動的に定義されるためです。

<span data-link="linkedFunction(a, b)"></span>
3. 戻り値を再評価するが、引数でデータリンクした値を受け取っていない場合

たとえば

var variable = {
    a: {
        b: {
            c: 'foo'
            d: 'bar'
        },
        e: 'bar'
    },
    func: function() {
        return this.a.b.c > this.a.b.d && this.a.e !== 'hoge';
    }
};

という階層を持ったオブジェクトをデータリンクしても、JsViews(実際はJsObservable)にとってはfuncメソッドがどういう依存関係を持っているのか分からないため、戻り値が再評価されません。この場合は先ほど説明した依存関係を定義する必要があります

依存関係の定義方法ですが、定義したいオブジェクトメソッドのdependsプロパティとして、

配列もしくは文字列として記述します。

[対象のメソッド].depends = [対象の変数, 依存するプロパティへのパス, ...]

後者のプロパティへのパスですが、先ほど出てきたオブジェクトの場合、プロパティcへのパスは

a.b.c

また、プロパティeへのパスは

a.e

といった具合になります。また、直下のすべてのプロパティを表す"*"(アスタリスク)を指定することもできます。

ここまで踏まえた依存関係の定義例は以下のとおりになります。それぞれ、いずれの書き方でもOKです。

// variableオブジェクトのプロパティ"e"が変更された場合にのみ
// variable.funcを再評価する
variable.func.depends = [variable, 'a.e'];
variable.func.depends = [variable.a, 'e'];
variable.func.depends = 'a.e';

// variableオブジェクトのプロパティ"c"または"d"が変更された場合に
// variable.funcを再評価する
variable.func.depends = [variable, 'a.b.c', 'a.b.d'];
variable.func.depends = [variable, 'a.b.*'];
variable.func.depends = [variable.a, 'b.c', 'b.d'];
variable.func.depends = [variable.a, 'b.*'];
variable.func.depends = [variable.a.b, 'c', 'd'];
variable.func.depends = [variable.a.b, '*'];
variable.func.depends = ['a.b.c', 'a.b.d'];
variable.func.depends = ['a.b.*'];

// variableオブジェクトのすべてのプロパティについて、変更があった
// 場合にvariable.funcを再評価する
variable.func.depends = [variable, 'a.e', 'a.b.c', 'a.b.e'];
variable.func.depends = [variable, 'a.*', 'a.b.*'];
variable.func.depends = ['a.e', 'a.b.c', 'a.b.e'];
variable.func.depends = ['a.*', 'a.b.*'];

JSFiddleで動作を見る

参照

2015-04-02

[]JsRenderチュートリアル - 存在しないプロパティを参照した時のエラーを回避する

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsRender入門 - Do You PHP はてなも参照してください。

今回やること

JsRenderやJsViewsで存在しないプロパティを参照した時のエラーを回避する

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result"></div>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
    // データの配列(もしくはオブジェクト)
    var fruits = {
            code: "01",
            name: "りんご",
            color: {
                name: "赤"
            },
            err: function() {
                return "エラーです";
            }
        };

    $.views.helpers({
        onerror: function(message) {
            return message + "です";
        }
    });

    // テンプレートに名前をつけて登録する
    $.templates({
        fruitsTemplate: "#template"
    });

    // テンプレート"fruitsTemplate"に変数fruitsを渡し、
    // レンダリング結果をid="result"に表示
    $("#result").html($.render.fruitsTemplate(fruits));
});
</script>

<script id="template" type="text/x-jsrender">
    <div>
        <li>{{>color.name}}</li>

        {{!--
            存在するオブジェクトのundefinedなプロパティを参照しても
            エラーにならず、空文字が表示される
        --}}
        <li>{{>color.foo}}</li>

        {{!--
            #data直下のundefinedなプロパティを参照してもエラーにならず、
            空文字が表示される
        --}}
        <li>{{>foo}}</li>

        {{!--
            undefinedのプロパティを参照すると

              {Error: TypeError: data.color.foo is undefined}

            というエラーになるが、onerrorを使うとエラー時に表示する
            メッセージを指定できる
        --}}
        <li>{{>color.foo.bar onerror="barがありません"}}</li>

        {{!-- リテラルだけではなくリンクした変数やヘルパーも使える --}}
        <li>{{>color.foo.bar onerror=color.name + "です"}}</li>
        <li>{{>color.foo.bar onerror=err()}}</li>
        <li>{{>color.foo.bar onerror=~onerror("ヘルパー")}}</li>
    </div>
</script>
</body>
</html>

説明

JsRenderやJsViewsで存在しないプロパティを参照した場合、

{Error: TypeError: data.foo.bar is undefined}

のようなメッセージが表示されることがありますが、このデフォルトのメッセージではなく任意のメッセージを表示させることができます。

JsRenderでの書式は次のとおりで、変数や式に続けて"onerror="と表示するメッセージを記述します。

{{>[変数や式] onerror=[表示するメッセージ]}}

表示するメッセージには、文字列のほか、テンプレートに渡された変数の値やヘルパーも使用できます。

JsViewsの場合、データリンクしたタグ({^{...}})のほかにdata-link属性にも記述可能です。

<li data-link="color.foo.bar onerror='エラーです'"></li>

JSFiddleで動作を見る

参照

2015-04-01

[]JsViewsチュートリアル - スタイルをリンクさせる

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsViews入門 - Do You PHP はてなも参照してください。

今回やること

JsViewsでスタイルと変数をリンクさせる

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result"></div>

<style type="text/css">
<!--
.box {
    border: 1px solid;
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
-->
</style>
<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// スタイルのリンク
$(function() {
    // 表示するデータ
    var data = {
        text: 'Hello !',
        width: 150,
        height: 100,
        bgcolor: 'lightgray'
    };

    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"と変数dataをリンク
    $.link.linkTemplate('#result', data);
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
    <div class="box" data-link="
        {:text}
        css-color{:bgcolor != '' ? 'white' : ''}
        css-background-color{:bgcolor}
        css-width{:width}
        css-height{:height}"
    >
    </div>
    <div>
        <p>表示するテキスト</p>
        <input type="text" data-link="text trigger=true"/>
    </div>
    <div>
        <p>幅</p>
        <select data-link="width">
        <option value="100">100</option>
        <option value="150">150</option>
        <option value="200">200</option>
        </select>
    </div>
    <div>
        <p>高さ</p>
        <select data-link="height">
        <option value="100">100</option>
        <option value="150">150</option>
        <option value="200">200</option>
        </select>
    </div>
    <div>
        <p>背景色</p>
        <select data-link="bgcolor">
        <option value="">なし</option>
        <option value="lightgray">lightgray</option>
        <option value="blue">blue</option>
        <option value="red">red</option>
        <option value="green">green</option>
        </select>
    </div>
</script>
</body>
</html>

説明

JsViewsチュートリアル - HTML要素の属性をデータリンクする - Do You PHP はてなでは属性と変数をリンクさせましたが、スタイルと直接リンクさせる事もできます。

書式ですが、属性名の代わりにプロパティ名に"css-"を付けたものになります。

data-link="css-[プロパティ名]{:[変数もしくは式]}

たとえば、colorプロパティであれば"css-color"、background-colorプロパティであれば"css-background-color"になります。

コード例では、

  • width
  • height
  • background-color

の3つを変数と直接リンクさせ、colorプロパティについてはbackground-colorプロパティの値が指定された場合に"white"となるように記述しています。

JSFiddleで動作を見る

参照

[]JsViewsチュートリアル - HTML要素の属性値のトグル

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsViews入門 - Do You PHP はてなも参照してください。

今回やること

JsViewsでHTML要素の属性値をデータリンクさせた変数の値を使ってトグルさせる

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result"></div>

<style type="text/css">
<!--
.label { font-weight: bold; }
.red { color: #e03fac; }
-->
</style>
<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// 属性のトグル
$(function() {
    // 表示するデータ
    var data = {
        mode: '1'
    };

    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"と変数dataをリンク
    $.link.linkTemplate('#result', data);
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
    <div>
        <p>現在のmode:<span data-link="mode"></span></p>
        <label for="mode1">
            <input type="radio" id="mode1" name="radio" value="1" data-link="mode"/>
            <span class="label" data-link="class{merge:mode == '1' toggle='red'}">ラジオ1</span>
        </label>
        <label for="mode2">
            <input type="radio" id="mode2" name="radio" value="2" data-link="mode"/>
            <span class="label" data-link="class{merge:mode == '2' toggle='red'}">ラジオ2</span>
        </label>
        <label for="mode3">
            <input type="radio" id="mode3" name="radio" value="3" data-link="mode"/>
            <span class="label" data-link="class{merge:mode == '3' toggle='red'}">ラジオ3</span>
        </label>
    </div>
</script>
</body>
</html>

説明

JsViewsチュートリアル - HTML要素の属性をデータリンクする - Do You PHP はてなでは属性値そのものををデータリンクした変数とリンクさせましたが、「変数が○○○の時だけ特定の属性を追加する」といった属性の値の一部をトグルさせる事もできます。

書式ですが、mergeコンバータを使って次のようになります。

data-link="[属性名]{merge:[変数もしくは式] toggle='[トグルさせる値]'}

変数もしくは式がtrueと判定された場合に限り、指定した属性名に"トグルさせる値"が追加されます。それ以外の場合は追加されません。

たとえば、「特定の場合だけclass属性に"active"というクラスを追加する」といった感じです。コード例では、ラジオボタンが選択された場合のみラベルに色を付けています。

なお、最初から指定されていた属性値はtoggleによる影響を受けません。コード例の場合だと、"label"クラスは上書きされずに残ります。

JSFiddleで動作を見る

参照

[]JsViewsチュートリアル - データリンクの方向性

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsViews入門 - Do You PHP はてなも参照してください。

今回やること

JsViewsのデータリンクが一方向・双方向の2種類あることを確認する

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result"></div>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// データリンクの方向性
$(function() {
    // 表示するデータ
    var message = {
        greet: 'Hello !'
    };

    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"と変数messageをリンク
    $.link.linkTemplate('#result', message);
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
    <div>
        <p><span data-link="greet"></span></p>

        {{!-- 変数名(triggerを含む)の前にのみ":"を付けた場合は一方向リンク --}}
        <p>一方向:data-link="{:greet trigger=true}"</p>
        <div><input data-link="{:greet trigger=true}"/></div>

        {{!-- 変数名(triggerを含む)の前後に":"を付けた場合は双方向リンク --}}
        <p>双方向:data-link="{:greet trigger=true:}"</p>
        <div><input data-link="{:greet trigger=true:}"/></div>

        {{!-- 変数名(triggerを含む)だけの場合は1番目の記述方法の省略形(双方向リンク) --}}
        <p>双方向:data-link="greet trigger=true"</p>
        <div><input data-link="greet trigger=true"/></div>
    </div>
</script>
</body>
</html>

説明

JsViewsチュートリアル - フォーム要素とデータリンク(チェックボックス+コンバータ) - Do You PHP はてなでも軽く触れましたが、JsViewsのdata-link属性に変数を記述する際、記述方法によって変更した値をリンクした変数に反映するかどうかが決まります。

JsViewsでのデータの流れは、先のエントリでも若干説明しましたが、大まかに以下のようになります。

変数 → [値] → [コンバータによる変換] → data-link属性を付けたHTML要素 → [変更された値] → [コンバータによる変換] → 変数

一方、data-link属性の値は、正しくは先のエントリにある

data-link="{[変数を取得する際のコンバータ]:[変数名]:[変数を書込む際のコンバータ]}"

という記述方法になります。各コンバータは省略可能です。先ほどのデータの流れに対応してるのが分かりますかね?

また、変数とコンバータの区切りに":"(コロン)を付けていますが、これが"どの方向に対してデータをリンクするか"を表しています。たとえば、上のように変数名の前後に":"を付けると、データの流れが

  • 変数→HTML要素
  • 変数←HTML要素

という双方向になります。これは双方向(two-way)データリンクと呼ばれます。

一方、後ろの":"以降を省略すると

data-link="{[コンバータA]:[変数名]}"

となりますが、データの流れは

変数 → [値] → [コンバータAによる変換] → data-link属性を付けたHTML要素

という具合に

  • 変数→HTML要素

という流れのみになるので、一方向(one-way)データリンクと呼ばれます。結果として、変更した値は変数に反映されません

また、

data-link="{:[変数名]:}"

とすれば

変数 → [値] → data-link属性を付けたHTML要素 → [変更された値] → 変数

になりますが、これは今までの説明で出てきた

data-link="[変数名]"

と等価になります。

なお、変数名の前の":"のみ省略する、ということはできません。

ちなみに、フォーム要素以外のHTML要素の場合、リンクされた値を直接変更する手段がありませんので、一方向・双方向のいずれの記述方法でも結果的に一方向となります。


JSFiddleで動作を見る

参照

2015-03-31

[]JsViewsチュートリアル - HTML要素の属性をデータリンクする

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsViews入門 - Do You PHP はてなも参照してください。

今回やること

JsViewsでHTML要素の属性をデータリンクさせる

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<p>テキストボックスに入力すると表示されたデータが更新される</p>
<div id="result"></div>

<style type="text/css">
<!--
.green { color: #3fe0ac; }
-->
</style>
<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// 属性をリンクさせる
$(function() {
    // 表示するデータ
    var message = {
        greet: 'Hello !',
        color: 'green',
        type: 'password',
        id: 'message',
        multiple: true
    };

    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"と変数messageをリンク
    $.link.linkTemplate('#result', message);
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
    <div>
        <p><span data-link="{:greet} class{:color}"></span></p>
        <div><input data-link="{:greet trigger=true} type{:type} id{:id}"/></div>

        <select id="select" data-link="multiple{:multiple}">
        <option value="1">セレクト1</option>
        <option value="2">セレクト2</option>
        <option value="3">セレクト3</option>
        </select>
    </div>
</script>
</body>
</html>

説明

1つ前のエントリと同様、JsViewsではフォーム要素のvalue値や可視性(visibility)だけではなく、classやtype、idなど他のHTML属性ともリンクさせることができます。

書式は次のようになります。1つ前のエントリの一般形ですね。

<[HTML要素名] ... data-link="[属性名]{:[変数もしくは式]} ..." />

また、以下のように複数同時に設定することも可能です。1つ前のエントリにあるvisibleも同様です。

<div data-link="id{:id} class{:color} visible{:visibility}">...</div>

ただし、"input要素のvalue属性の値と、他の属性値を同時に指定したい"といった場合、前者の書き方を以下のようにする必要があります。

{:[変数名]:}

これを

{:[変数名]}

としてしまうと、フォーム要素で変更した値がリンクした変数に反映されません。一方、span要素やdiv要素など、値を変更することができない要素の場合はいずれの記述でも問題ありません。

JSFiddleで動作を見る

参照

[]JsViewsチュートリアル - visibilityをリンクする

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsViews入門 - Do You PHP はてなも参照してください。

今回やること

JsViewsで変数の値と可視性(visibility)をリンクさせる

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<p>テキストボックスに入力すると表示されたデータが更新される</p>
<div id="result"></div>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// visibilityをリンクさせる
$(function() {
    // 表示するデータ
    var message = {
        greet: 'Hello !'
    };

    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"と変数messageをリンク
    $.link.linkTemplate('#result', message);
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
    <div>
        <p><span class="red" data-link="visible{:greet == 'Hello'}">"Hello"以外を入力してください</span></p>
        <div><input type="text" data-link="greet trigger=true"/></div>
    </div></script>
</body>
</html>

説明

JsViewsではフォーム要素のvalue値だけではなく、可視性(visibility)とリンクさせることができます。

書式は次のようになります。

<[HTML要素名] ... data-link="visibile{:[変数もしくは式]}" />

JSFiddleで動作を見る

参照

[]JsViewsチュートリアル - フォーム要素とデータリンク(チェックボックス+コンバータでvalue属性の値を使う)

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsViews入門 - Do You PHP はてなも参照してください。

今回やること

JsViewsでチェックボックスをデータリンクさせ、コンバータで変数に格納する値をvalue属性の値に変更する

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result"></div>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// フォーム要素とデータリンク
$(function() {
    $.views.converters({
        preCheckboxWithValue: function (value) {
//            console.log(this);
            var v = this.linkCtx.elem.value ? this.linkCtx.elem.value : null;
            return value === v ? true : false;
        },
        postCheckboxWithValue: function (value) {
//            console.log(this);
            var v = this.linkCtx.elem.value ? this.linkCtx.elem.value : null;
            return value == true ? v : null;
        }
    });

    // 表示するデータ
    var message = {
        check_as_value: null
    };

    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"と変数messageをリンク
    $.link.linkTemplate('#result', message);
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
    <p>check_as_value=<span data-link="check_as_value"></span></p>

    <div>
        <label for="check_as_value"><input id="check_as_value" type="checkbox" value="bar" data-link="{preCheckboxWithValue:check_as_value:postCheckboxWithValue}"/>チェックボックス</label>
    </div>
</script>
</body>
</html>

説明

1つ前のエントリではコンバータを使ってチェックボックスとデータリンクさせた場合の値を"0"と"1"に変換する例を見てきましたが、どうせならチェックボックスのvalue属性の値にしたいですよね?

コンバータとして定義した関数内では、自身を表す"this"を使って現在対象となっている要素やViewオブジェクトなどの"コンテキスト情報"を取得することができます。

たとえば、現在のコンバータが対象としている変数はtagCtx(タグコンテキスト)を使って

this.tagCtx.params.args

その値は

this.tagCtx.args

また、リンクされた変数の値は

this.linkCtx._val

といった具合です。実際にコンバータ関数の中でconsole.log()等で値を確認してみると良いです。

で、この中に、現在コンバータが対象としているHTML要素も

this.linkCtx.elem

で取得でき、そのvalue属性の値を

this.linkCtx.elem.value

で参照できます。これを使うことでvalue属性の値を結果の値とすることができます。

JSFiddleで動作を見る

参照

[]JsViewsチュートリアル - フォーム要素とデータリンク(チェックボックス+コンバータ)

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsViews入門 - Do You PHP はてなも参照してください。

今回やること

JsViewsでチェックボックスをデータリンクさせ、コンバータで変数に格納する値を変更する

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result"></div>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// フォーム要素とデータリンク
$(function() {
    $.views.converters({
        preCheckbox: function (value) {
            return value == '1' ? true : false;
        },
        postCheckbox: function (value) {
            return value == true ? '1' : '0';
        }
    });

    // 表示するデータ
    var message = {
        check_as_value: '0'
    };

    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"と変数messageをリンク
    $.link.linkTemplate('#result', message);
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
    <p>check=<span data-link="check"></span></p>

    <div>
        <label for="check_as_value"><input id="check_as_value" type="checkbox" data-link="{preCheckbox:check_as_value:postCheckbox}"/>チェックボックス</label>
    </div>
</script>
</body>
</html>

説明

1つ前のエントリではチェックボックスとデータリンクさせた場合、値がbool型(true・false)になることを見てきましたが、"0"と"1"で表したほうが良い場合が多々あると思います。この場合、コンバータを使うことで解決できます。

これまでは、表示形式を変更するためにコンバータを使ってきましたが、リンクした値を変更する場合にも使えます。また、

  • リンクした変数から値を取得する
  • リンクした変数に値を格納する

という2つのタイミングでコンバータを使用できます。イメージ的には次のようなフローになります。

変数 → [値を取得] → input要素 → [値を格納] → 変数

2つコンバータを使用する場合のテンプレート側の書き方は以下のようになります。

{[コンバータ(前)]:[変数]:[コンバータ(後)]}

先ほどのフローを書き換えると次のようになります。

変数 → [コンバータ(前)] → input要素 → [コンバータ(後)] → 変数

たとえば、今回のチェックボックスの場合、input要素ではtrue・falseでチェックボックスのオン・オフを切り替えますが、最終的な値は1か0としたいので、

  • コンバータ(前)では、1・0をそれぞれtrue・falseに変換する
  • コンバータ(後)では、true・falseをそれぞれ1・0に変換する

とすれば良い、ということになります。

変数 → [1・0をtrue・falseに変換] → input要素 → [true・falseを1・0に変換] → 変数

コンバータの書式はJsRenderチュートリアル - 独自のコンバータを作る - Do You PHP はてなと同じです。以下のpreCheckbox、postCheckboxというコンバータが、それぞれコンバータ(前)、コンバータ(後)に相当します。

$.views.converters({
    preCheckbox: function (value) {
        return value == '1' ? true : false;
    },
    postCheckbox: function (value) {
        return value == true ? '1' : '0';
    }
});

また、テンプレート側は次のように変数check_as_valueがpreCheckboxとpostCheckboxに挟まれた形になります。

<input id="check_as_value" type="checkbox" data-link="{preCheckbox:check_as_value:postCheckbox}"/>

JSFiddleで動作を見る

参照

[]JsViewsチュートリアル - フォーム要素とデータリンク(チェックボックス)

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsViews入門 - Do You PHP はてなも参照してください。

今回やること

JsViewsでチェックボックスをデータリンクさせる

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result"></div>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// フォーム要素とデータリンク
$(function() {
    // 表示するデータ
    var message = {
        check: false
    };

    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"と変数messageをリンク
    $.link.linkTemplate('#result', message);
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
    <p>check=<span data-link="check"></span></p>

    <div>
        <label for="check"><input id="check" type="checkbox" data-link="check"/>チェックボックス</label>
    </div>
</script>
</body>
</html>

説明

フォーム要素と変数の値をデータリンクさせたい場合、基本的には、JsViewsチュートリアル - まずはHello World - Do You PHP はてなで出てきたdata-link属性を使ってリンクしたい変数を指定します。今回はチェックボックス(input要素のtype属性が"checkbox")の例です。

基本的にはselect要素やラジオボタンと同様、チェックボックスにdata-link属性を付けてリンクさせる変数を指定します。

<input id="check" type="checkbox" data-link="check"/>

ブラウザ上から選択された項目が変更されたタイミングで変数の値が変化します。ただし、値がvalue属性の値ではなくbool型(true/false)になることに注意してください。


JSFiddleで動作を見る

参照

[]JsViewsチュートリアル - フォーム要素とデータリンク(ラジオボタン)

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsViews入門 - Do You PHP はてなも参照してください。

今回やること

JsViewsでラジオボタンをデータリンクさせる

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result"></div>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// フォーム要素とデータリンク
$(function() {
    // 表示するデータ
    var message = {
        radio: '1'
    };

    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"と変数messageをリンク
    $.link.linkTemplate('#result', message);
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
    <p>radio=<span data-link="radio"></span></p>

    <div>
        <label for="radio1"><input id="radio1" type="radio" name="radio" value="1" data-link="radio"/>ラジオ1</label>
        <label for="radio2"><input id="radio2" type="radio" name="radio" value="2" data-link="radio"/>ラジオ2</label>
        <label for="radio3"><input id="radio3" type="radio" name="radio" value="3" data-link="radio"/>ラジオ3</label>
    </div>
</script>
</body>
</html>

説明

フォーム要素と変数の値をデータリンクさせたい場合、基本的には、JsViewsチュートリアル - まずはHello World - Do You PHP はてなで出てきたdata-link属性を使ってリンクしたい変数を指定します。今回はラジオボタン(input要素のtype属性が"radio")の例です。

基本的にはselect要素と同様、ラジオボタンにdata-link属性を付けてリンクさせる変数を指定します。通常、複数のラジオボタンを用意すると思いますが、そのいずれにもdata-link属性と同じ変数を指定します。

<input type="radio" name="radio" value="1" data-link="radio"/>
<input type="radio" name="radio" value="2" data-link="radio"/>
<input type="radio" name="radio" value="3" data-link="radio"/>

ブラウザ上から選択された項目が変更されたタイミングで変数の値が変化し、値はvalue属性の値になります。


JSFiddleで動作を見る

参照

[]JsViewsチュートリアル - フォーム要素とデータリンク(select要素)

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsViews入門 - Do You PHP はてなも参照してください。

今回やること

JsViewsでselect要素をデータリンクさせる

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result"></div>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
    // 表示するデータ
    var message = {
        select: '2',
        multiple: [1, 3]
    };

    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"と変数messageをリンク
    $.link.linkTemplate('#result', message);
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
    <p>select=<span data-link="select"></span></p>
    <p>multiple=<span data-link="multiple"></span></p>

    <div>
        <select id="select" data-link="select">
            <option value="1">セレクト1</option>
            <option value="2">セレクト2</option>
            <option value="3">セレクト3</option>
        </select>
    </div>
    <div>
        <select id="multiple" multiple data-link="multiple">
            <option value="1">マルチプル1</option>
            <option value="2">マルチプル2</option>
            <option value="3">マルチプル3</option>
        </select>
    </div>
</script>
</body>
</html>

説明

フォーム要素と変数の値をデータリンクさせたい場合、基本的には、JsViewsチュートリアル - まずはHello World - Do You PHP はてなで出てきたdata-link属性を使ってリンクしたい変数を指定します。今回はselect要素(プルダウン)の例で、単一選択と複数選択の2種類の動作が確認できるサンプルです。

select要素にdata-link属性を付け、リンクさせる変数を指定します。

<select id="select" data-link="select"></select>

ブラウザ上から選択された項目が変更されたタイミングで変数の値が変化し、値はoption要素のvalue属性の値(文字列)になります。また、multiple属性を付けた場合も同様ですが、変数の値は文字列配列、非選択時はnullになります。


JSFiddleで動作を見る

参照

2015-03-30

[]PECL::ssh2を使ってみる

このエントリは、Do You PHP?(www.doyouphp.jp)で公開していたコンテンツを移行/加筆/修正したものです。公開の経緯はこちらをどうぞ。目次はこちらです。

初出:2006/01/28

PHPを使ってバッチスクリプトなどを作る際、ファイル転送といえば今まではftp経由が主流(?)かと思いますが、昨今のセキュリティに対する意識向上から何らかの暗号化を施したい場合があります。2005年の初めにPECLにssh2拡張モジュールが登録されマニュアルもそれなりにこなれてきましたので、今更ながら試してみました。

今回のゴールは、PHPから公開鍵認証を行い、リモートコマンドの実行とファイル転送を行う、というものです。

インストール

まずはインストールした環境ですが、OSはCentOS6.4、PHPは以下のようなconfigureオプションを付けたPHP5.6.6です。

./configure  \
--with-apxs2=/usr/local/apache2/bin/apxs \
--prefix=/usr/local/lib/php56 \
--with-pgsql=shared,/usr/local/pgsql \
--with-gd \
--with-jpeg-dir \
--with-png-dir \
--with-xpm-dir \
--with-freetype-dir \
--enable-gd-native-ttf \
--enable-gd-jis-conv \
--enable-dom \
--with-zlib-dir \
--enable-mbstring \
--enable-pdo=shared \
--with-pdo-pgsql=shared,/usr/local/pgsql \
--with-openssl \
--with-pear=/usr/local/lib/php56/pear \
--with-pdo-sqlite=shared \
--with-curl=shared \
--with-mcrypt=shared \
--enable-intl \
--enable-opcache \
--enable-zip=shared \
--disable-ipv6 \
--with-iodbc=shared \
--with-pdo-odbc=shared,iODBC \
--enable-pcntl=shared \
--enable-soap=shared \
--with-readline

「--prefix」オプションを付けているため、phpコマンドのパスがデフォルトとは変わっていますが適宜読み替えてください。

さて、ssh2拡張モジュールのインストールですが、PHPマニュアルにもあるとおり先にopensslとlibssh2をインストールしておく必要があります。なお、最近のLinuxディストリビューションであれば、openssl-develパッケージなど用意されていると思いますので、aptなりup2dateなりyumなりでインストールした方が簡単です。今回の環境ではyumコマンドで各パッケージをインストールしました。

$ sudo yum install -y openssl openssl-devel libssh2 libssh2-devel
$ 

続いて、peclコマンドでssh2拡張モジュールをインストールします。

$ sudo /usr/local/lib/php56/bin/pecl install -a ssh2-beta
$ 

インストールが終わったら、ssh2拡張モジュールをロードするようphp.iniに追記します。

            :
extension=ssh2.so
            :

最後にモジュールが正しくロードされている事を確認します。ApachePHPで利用する場合はApache再起動します。

$ /usr/local/lib/php56/bin/php -m | grep ssh2
ssh2
$ 

接続の手順

接続の手順は大まかに

  1. ssh2_connect関数を使用してSSHサーバに接続
  2. 認証用の関数を使用して認証

となります。認証方法にはパスワード認証と公開鍵認証が利用できます。

パスワード認証の例

まず、パスワード認証の場合、ssh2_auth_password関数を使用します。

<?php
$connection = ssh2_connect('targethost', 22);
if (!ssh2_auth_password($connection, 'username', 'password')) {
    die('認証失敗');
}
echo "認証成功" . PHP_EOL;

公開鍵認証の例

公開鍵認証の場合、事前にRSA秘密鍵・公開鍵のペアを作成しておく必要が(当然)あります。また、Apacheと組み合わせる場合はUserディレクティブで指定したユーザー、バッチの場合はバッチを実行するユーザーでの読み込み権限が必要です。今回は、/home/httpd/.sshディレクトリを作成し、所有者をApacheのUserディレクティブでしていたnobodyユーザーにしました。使用する関数は、ssh2_auth_pubkey_fileです。

以下ざっとした手順です。targethostの/etc/ssh/sshd_configの編集が必要になる場合もありますので、適宜Googleで検索してみてください。

$ ssh-keygen -t rsa -b 2048
Generating public/private rsa key pair.
Enter file in which to save the key (/home/somebody/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/somebody/.ssh/id_rsa.
Your public key has been saved in /home/somebody/.ssh/id_rsa.pub.
The key fingerprint is:
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX somebody@client
$ 
$ sudo mkdir -p /home/httpd/.ssh
$ sudo cp -p ~somebody/.ssh/id_rsa* /home/httpd/.ssh/
$ sudo chown -R nobody /home/httpd/
$ sudo chmod go-rwx /home/httpd/.ssh/
$ 

公開鍵を接続先サーバにコピーしauthorized_keys2を作成しておきます。

$ scp /home/somebody/.ssh/id_rsa.pub targethost:./
somebody@targethost's password:
$ ssh targethost
somebody@targethost's password:
$ mkdir .ssh
$ chmod 700 .ssh/
$ mv id_rsa.pub .ssh/authorized_keys2
$ 

さて、PHPコードのほうですが、次のような感じになります。

<?php
$connection = ssh2_connect('targethost', 22, array('hostkey'=>'ssh-rsa'));

/**
 * 公開鍵・秘密鍵の読込権限に注意
 */
if (!ssh2_auth_pubkey_file($connection, 'somebody',
                          '/home/httpd/.ssh/id_rsa.pub',
                          '/home/httpd/.ssh/id_rsa',
                          'passphrase')) {
    die('認証失敗');
}
echo "認証成功" . PHP_EOL;

リモートコマンドの実行例

コマンド実行はssh2_exec関数で行いますが、結果出力を取得する場合はssh2_exec関数から返されるストリームのブロックモードを有効にしておく必要があります。

<?php
               :
/**
 * dfコマンドの実行結果を取得する
 */
$stream = ssh2_exec($connection, 'df -h ');

/**
 * ストリームのブロックモードを有効にし、ストリームからデータを読み出せる
 * 状態になるまで待つ
 */
stream_set_blocking($stream, true);

var_dump(fread($stream, 4096));

ちなみに、ここで返されるストリームはstdoutになります。このため、エラーメッセージなどを取得する場合は、コマンドの後に「2>&1」を付けてstderrと共にstdoutに出力をするか、以下のように一度stderrサブストリームを取得して、結果を取得するようになります。

<?php
               :
/**
 * 存在しないコマンドの実行結果を取得する
 */
$stdout_stream = ssh2_exec($connection, 'non-exist-command ');

/**
 * stderrサブストリームを取得する。ブロックモードへの変更も忘れずに!
 */
$stderr_stream = ssh2_fetch_stream($stdout_stream, SSH2_STREAM_STDERR);
stream_set_blocking($stdout_stream, true);

var_dump(fread($stdout_stream, 4096));
var_dump(fread($stderr_stream, 4096));

ファイルの転送例

これはPHPマニュアルどおり、コマンド一発で行えます。

<?php
              :
if (ssh2_scp_send($connection,
                  '/home/httpd/test.log',
                  '/home/shimooka/test.log', 0644)) {
    echo '転送成功';
} else {
    echo '転送失敗';
}

まとめ

前から知っていたのですが、試してみるのが今頃になってしまいました。今回試してみて公開鍵認証が問題なく行えると分かりました。これは使いどころによってはかなり強力ではないかと思います。

通常、バッチスクリプトならshスクリプトで済ませてしまうことが多いのですが、ちょっと凝ったことをするバッチなどに良いかも知れません。

2015-03-27

[]JsViewsチュートリアル - データリンクされたオブジェクトを操作する

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsViews入門 - Do You PHP はてなも参照してください。

今回やること

JsViewsでデータリンクされたオブジェクトに対して更新・削除などの操作をする

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result"></div>

<a href="#" id="add">add</a>
<a href="#" id="modify">modify</a>
<a href="#" id="reset">reset</a>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// オブジェクトの操作
$(function() {
    // 表示するデータ(オブジェクト)
    var fruits = { name: "りんご", num: 3 };

    // テンプレートに名前をつけて登録する
    // linkPropsTemplateはデータリンクしたpropsタグを使ったテンプレート
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"と変数fruitsをリンク
    $.link.linkTemplate('#result', fruits);

    $(document)
        /**
         * "add"をクリックすると、オブジェクトに新しいプロパティを
         * 追加して再レンダリング
         */
        .on('click', '#add', function(ev) {
            $.observable(fruits).setProperty('color', 'green');
            ev.preventDefault();
        })
        /**
         * "modify"をクリックすると、既存のプロパティを更新して
         * 再レンダリング
         */
        .on('click', '#modify', function(ev) {
            if (fruits.name) {
                $.observable(fruits).setProperty('name', 'ドリアン');
            }
            ev.preventDefault();
        })
        /**
         * "remove"をクリックすると、プロパティを削除して
         * 再レンダリング
         */
        .on('click', '.remove', function(ev) {
            // removePropertyはドキュメント化されていないメソッド
            $.observable(fruits).removeProperty(
                // clickイベントが発生したViewオブジェクトを取得し、
                // そのデータのキーを取得する
                $.view(this).data.key
            );
            ev.preventDefault();
        })
        /**
         * "reset"をクリックすると、既存のプロパティを更新して
         * 再レンダリング
         */
        .on('click', '#reset', function(ev) {
            if (fruits.color) {
                $.observable(fruits).removeProperty('color');
            }
            $.observable(fruits).setProperty({
                name: "りんご",
                num: 3
            });
            ev.preventDefault();
        });
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
{^{props #data}}
    <p>{^{>key}}の値は{^{:prop}}です <a href="#" class="remove">×</a></p>
{{/props}}
</script>
</body>
</html>

説明

前回は配列の操作でしたので、今回はリンクされたオブジェクトの操作の説明です。

JsViews(実際にはJsObservableという別ライブラリ)では、リンクされたオブジェクトについては、更新と削除の2つのメソッドが用意されています。

更新

オブジェクトプロパティを1つまたは複数同時に更新します。複数更新する場合は、オブジェクトとして渡します。

$.observable([データリンクしたオブジェクト]).setProperty([プロパティ名], [プロパティの値])
$.observable([データリンクしたオブジェクト]).setProperty({
    [プロパティ名1]: [プロパティの値1],
    [プロパティ名2]: [プロパティの値2],
          :
})

コード例では、"add"というリンクをクリックした時に、変数fruitsに"color"というプロパティを追加しています。

$.observable(fruits).setProperty('color', 'green');

また、"modify"というリンクをクリックした時には、nameプロパティの値を"ドリアン"に変更しています。

$.observable(fruits).setProperty('name', 'ドリアン');

もう一つ。"reset"というリンクをクリックした時には、name, numの各プロパティの値を同時に変更しています。

$.observable(fruits).setProperty({
    name: "りんご",
    num: 3
});
削除

公式サイトにドキュメント化されていませんが、プロパティを削除するメソッドも用意されています。

$.observable([データリンクしたオブジェクト]).removeProperty([プロパティ名])

コード例では、"×"というリンクをクリックした際に、当該プロパティを変数fruitsから削除しています。

$.observable(fruits).removeProperty(
    $.view(this).data.key
);

なお、該当するプロパティ名は、クリックされた要素を含むViewオブジェクトを取得し、そのデータから取得しています。

JSFiddleで動作を見る

参照

[]JsViewsチュートリアル - データリンクされた配列データを操作する

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsViews入門 - Do You PHP はてなも参照してください。

今回やること

JsViewsでデータリンクされた配列データに対して追加・削除などの操作をする

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result_link"></div>
<hr/>
<div id="result_link_for"></div>

<a href="#" id="add">add</a>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// 配列の操作
$(function() {
    // データの配列(もしくはオブジェクト)
    var fruits = [
        { name: "りんご", num: 3 },
        { name: "いちご", num: 5 },
        { name: "バナナ", num: 2 },
    ];

    // テンプレートに名前をつけて登録する
    // linkForTemplateはデータリンクしたforタグを使ったテンプレート
    $.templates({
        linkTemplate: "#template",
        linkForTemplate: "#template_for"
    });

    // テンプレート"linkTemplate"と変数fruitsをリンク
    $.link.linkTemplate('#result_link', fruits);
    $.link.linkForTemplate('#result_link_for', {fruits: fruits});

    $(document)
        /**
         * "add"をクリックすると、配列データに新しいデータを
         * 追加して再レンダリング
         */
        .on('click', '#add', function(ev) {
            // リンクされた配列データへの追加は$.observable().insert()
            $.observable(fruits).insert(
                { name: "ドリアン", num: fruits.length }
            );
            ev.preventDefault();
        })
        /**
         * "×"をクリックすると、配列データから当該データを
         * 削除して再レンダリング
         */
        .on('click', '.remove', function(ev) {
            // リンクされた配列データから削除は$.observable().remove()
            $.observable(fruits).remove(
                // clickイベントが発生したViewオブジェクトを取得し、
                // そのindexを取得する
                $.view(this).index
            );
            ev.preventDefault();
        });
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
    <p>{^{>#index}}:{{:name}}{{:num}}個あります <a href="#" class="remove">×</a></p>
</script>
<script id="template_for" type="text/x-jsrender">
{^{for fruits}}
    <p>{^{>#index}}:{{:name}}{{:num}}個あります <a href="#" class="remove">×</a></p>
{{/for}}
</script>
</body>
</html>

説明

前回までのチュートリアルでは、テキストボックスに値を入力することでデータリンクした値を更新していましたが、当然JavaScriptコード内からデータを更新したい場合もあります。

例えば配列データの場合、

fruits.push({ name: "ドリアン", num: 4 });

などとやっても、JsViewsにはデータが更新されたかどうか判断できません。逆に、リンクの情報を壊してしまう結果になります。JsViewsでは、配列オブジェクトを更新するための仕組みが提供されています(実際にはJsObservableという別ライブラリ)ので、これを使うことになります。配列データの更新については、追加・削除・移動・全更新の4つが用意されています。

追加

配列データの任意の場所、もしくは最後にデータを追加します。

$.observable([データリンクした配列データ]).insert([追加するデータの位置], [追加するデータ])

追加するデータの位置は配列の添字(0始まり)です。省略可能でその場合は最後に追加されます。

コード例では、"add"というリンクをクリックした際に、変数fruitsの最後にオブジェクトを追加しています。

$.observable(fruits).insert({ name: "ドリアン", num: fruits.length });
削除

配列データの任意の場所のデータを削除します。

$.observable([データリンクした配列データ]).remove([削除するデータの位置], [削除するデータの数])

削除するデータの位置は配列の添字(0始まり)で、削除した位置より後ろのデータは前に詰められます。削除するデータ数は省略した場合は1となります。

コード例では、"×"というリンクをクリックした際に、当該データを変数fruitsから削除しています。

$.observable(fruits).remove($.view(this).index);
移動

配列データの任意の場所のデータを任意の場所に移動します。

$.observable([データリンクした配列データ]).move([移動するデータの位置], [移動先の位置], [移動するデータの数])

移動するデータの位置と移動先の位置は配列の添字(0始まり)です。移動するデータの数を省略した場合は1となります。

全更新

配列データをすべて入れ替えます。

$.observable([データリンクした配列データ]).refresh([新しい配列データ])

JSFiddleで動作を見る

参照

[]JsViewsチュートリアル - タグをデータリンクさせる

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsViews入門 - Do You PHP はてなも参照してください。

今回やること

JsViewsでタグをデータリンクさせた場合の動作を確認する

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<p>テキストボックスに入力すると表示されたデータが更新される</p>
<div id="result"></div>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// タグのリンク
$(function() {
    // 表示するデータ
    var message = {
        greet: 'Hello !'
    };

    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"と変数greetをリンク
//    $.templates('#template').link('#result', message);
    $.link.linkTemplate('#result', message);
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
    <div>
        <p>タグにキャレットなし</p>
        <p>{{if greet != 'Hello'}}{^{>greet}}{{/if}}</p>
    </div>
    <div>
        <p>タグにキャレットあり</p>
        {{!-- タグに"^"(キャレット/サーカムフレックス)が付いていることに注意 --}}
        <p>{^{if greet != 'Hello'}}{^{>greet}}{{/if}}</p>
        <div><input type="text" data-link="greet trigger=true"/></div>
    </div>
</script>
</body>
</html>

説明

先日のチュートリアルでは、値を表示する場合に"^"(キャレット/サーカムフレックス)を付けるパターンが出てきましたが、これはifやforなどのタグにも使えます

書式は値の場合と同様で、

{^{[タグ名] ... }} ... {{/[タグ名]}}

のようになります。具体的には

{^{if ... }}
    :
{{/if}}

といった具合です。

こうすると、データが更新された場合にタグも再評価され、入力フォームなどに入力された値と連動させることができるようになります。

コード例では、テキストボックスに入力した値が"Hello"と一致した場合は表示しない、というものです。その他の値の場合は、先日のチュートリアルと同様に表示されます。

JSFiddleで動作を見る

参照

2015-03-24

[]JsRenderチュートリアル - for、propsタグとelseタグ

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsRender入門 - Do You PHP はてなも参照してください。

今回やること

テンプレート内のfor、propsタグとelseタグを組み合わせて使ってみる

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result"></div>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// for、propsタグとelseタグ
$(function() {
    // 表示するデータ
    var data = [
        {
            color: "赤",
            fruits: [
                { name: "りんご", num: 3 },
                { name: "いちご", num: 5 },
                {}
            ]
        },
        {
            color: "緑",
        },
        {
            color: "黄",
            fruits: [
                { name: "バナナ", num: 2 }
            ]
        }
    ];

    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"に変数dataを渡し、
    // レンダリング結果を表示
    $("#result").html($.render.linkTemplate(data));
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
    {{for #data tmpl="#template_item"/}}
</script>
<script id="template_item" type="text/x-jsrender">
    <h3>{{>color}}色</h3>
    {{for fruits}}
        {{props #data}}
            <p>{{>key}}の値は{{>prop}}です</p>
        {{else}}
            空っぽです!
        {{/props}}
    {{else}}
        何もありません
    {{/for}}
</script>
</body>
</html>

説明

forタグやpropsタグ、カスタムタグはelseタグと組み合わせて使うことができます。データが存在しない場合のメッセージなど、elseタグ側に条件を満たさない場合の内容を記述します。

    {{for [配列]}}
        :
    {{else}}
        :
    {{/for}}

    {{props [オブジェクト]}}
        :
    {{else}}
        :
    {{/prop}}

JSFiddleで動作を見る

参照

[]JsRenderチュートリアル - if、for、propsタグで子テンプレートを読み込む

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsRender入門 - Do You PHP はてなも参照してください。

今回やること

テンプレート内のif、for、propsタグで子テンプレートを指定して値をレンダリングする

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result"></div>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// if、for、propsタグで子テンプレートを読み込む
$(function() {
    // 表示するデータ
    var data = {
        items: [
            { name: "りんご", num: 3 },
            { name: "いちご", num: 5 },
            { name: "バナナ", num: 2 }
        ],
        options: {
            library: "JsRender",
            language: "JavaScript"
        }
    };

    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"に変数dataを渡し、
    // レンダリング結果を表示
    $('#result').html($.render.linkTemplate(data));
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
{{for items tmpl="#template_for"/}}
{{props options tmpl="#template_props"/}}
</script>
<script id="template_for" type="text/x-jsrender">
    <p>{{:name}}{{:num}}個あります</p>
    {{if num < 3 tmpl="#template_if" /}}
</script>
<script id="template_if" type="text/x-jsrender">
    <p>{{:name}}はそろそろ少なくなってきました</p>
</script>
<script id="template_props" type="text/x-jsrender">
    <p>{{>prop}}の値は{{>key}}です</p>
</script>
</body>
</html>

説明

ifタグ(elseも含む)、forタグ、propsタグは、タグ中にデータをレンダリングするテンプレートを指定することができます。この場合、いずれもtmpl属性に続いてテンプレートへのセレクタ(もしくはテンプレート文字列)を指定します。

    {{if [条件式] tmpl="[テンプレートへのセレクタ]" /}}

    {{if [条件式] tmpl="[テンプレートへのセレクタ]"}}
         :
    {{else [条件式] tmpl="[テンプレートへのセレクタ]"}}
         :
    {{else tmpl="[テンプレートへのセレクタ]"}}
         :
    {{/if}}

    {{for [配列] tmpl="[テンプレートへのセレクタ]" /}}

    {{props [オブジェクト] tmpl="[テンプレートへのセレクタ]" /}}

JSFiddleで動作を見る

参照

[]JsViewsチュートリアル - まずはHello World

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsViews入門 - Do You PHP はてなも参照してください。

今回やること

JsViewsでテキストボックスに入力されたメッセージを表示する

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<p>JsRender</p>
<p>テキストボックスに入力しても表示されたデータは更新されない</p>
<div id="result_render"></div>
<p>JsViews</p>
<p>テキストボックスに入力すると表示されたデータが更新される</p>
<div id="result_link"></div>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
    // 表示するデータ
    var message = {
        greet: 'Hello !'
    };

    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"に変数greetを渡し、
    // レンダリング結果を表示
    $('#result_render').html($.render.linkTemplate(message));

    // テンプレート"linkTemplate"と変数greetをリンク
    $.link.linkTemplate('#result_link', message);
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
    {{!-- "^"(キャレット/サーカムフレックス)が付いていることに注意 --}}
    <p>{^{>greet}}</p>

    <div>
        <p>triggerなし</p>
        {{!-- data-link属性に注目! --}}
        <input type="text" data-link="greet"/>
    </div>

    <div>
        <p>trigger=true</p>
        {{!-- data-link属性の"trigger=true"に注目! --}}
        <input type="text" data-link="greet trigger=true"/>
    </div>
</script>
</body>
</html>

説明

JsViewsは単純なテンプレートエンジンではなく、

テンプレートに渡した値と表示された値が同期(リンク)されている

という特徴があります。

上のコード例は、テンプレートとリンクさせた変数をテキストボックスの値とし、その値を表示させるサンプルになります。JSFiddleで動作を見ると、

  • テキストボックスの値を変更してフォーカスを外す(blur)
  • テキストボックスの値を変更したタイミング

と表示されている値が変わる2種類があることが確認できると思います。

JsViewsを使用する際の基本的な流れはJsRenderの流れと似ており、以下のようになります。

1. JsViewsファイルを読み込む

ここはJsRenderの時と同じです。

2. テンプレートを用意する

基本的にはJsRenderと同じなのですが、well-formedである必要があります。ここで言うwell-formedとは、

HTML要素だけがwell-formedと言うわけではなく、HTML要素とテンプレートのタグの組み合わせでwell-formed

ということです。したがって、JsViewsのテンプレートでは、属性の値にifタグなどを使うことはできません。この場合、別途用意された方法を使用する必要があります。

さて、リンクされたデータを表示する方法についてですが、大きく2種類あります。

1つ目はJsRenderで使っていた書式に"^"(キャレット/サーカムフレックス)が付くようになります。例えば、上のコード例でgreetという変数を表示する場合、

{^{>greet}}

と記述します。

もう1つは、HTMLタグに"data-link"という特別な属性をつけることで、そのタグの値を変数の値にする、というものです。コード例では以下のように記述しています。

    <input type="text" data-link="greet">

ここではとりあえず、フォーム要素(inputタグやselectタグなど)の場合はdata-link属性の値に変数名を指定すると値がリンクされる、と覚えておいてください。

また、data-link属性の値に変数名の他に"trigger=true"を指定すると、入力したタイミングでデータが更新されるようになります。

    <input type="text" data-link="greet trigger=true">
3. JavaScript内で表示したい値を用意する

コード例では、比較のために

としています。

4. JsViewsが提供しているlinkメソッド引数としてリンクさせたい変数を渡してテンプレートとリンクする

JavaScriptコード内にある

    $.link.linkTemplate('#result_link', message);

がこれに相当します。いきなり名前付きテンプレートを使っていますが、名前付きテンプレートを使わない場合は

    $.templates([テンプレートへのセレクタ]).link([表示スペースへのセレクタ], [リンクする変数]);

のようになります。具体的には

    $.templates('#template').link('#result_link', message);

となります。

JSFiddleで動作を見る

参照

[]JsRenderチュートリアル - テンプレートヘルパーを渡す

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsRender入門 - Do You PHP はてなも参照してください。

今回やること

ヘルパーの定義と使い方の説明

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result"></div>

<style type="text/css">
<!--
.blue { color: #3face0; }
.red { color: #e03fac; }
-->
</style>
<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="js/jsrender.min.js" type="text/javascript"></script>
<script type="text/javascript">
// ヘルパーの定義と使い方
$(function() {
    // 表示するデータ
    var items = [
        { name: "りんご", num: 3 },
        { name: "いちご", num: 5 },
        { name: "バナナ", num: 2, unit: '本' }
    ];

    // ヘルパーの定義
    var helper = {
        unit: function(data) {
            if (data.unit === undefined) {
                return 'こ';
            }

            return data.unit;
        },
        color: "blue"
    };

    // ヘルパーをJsRenderに登録する場合(1)
    //
    // ただし、render時にヘルパーが渡された場合、
    // 同名のメソッドやプロパティは上書きされる
    $.views.helpers({
        unit: function(data) {
            if (data.unit === undefined) {
                return '個';
            }

            return data.unit;
        },
        color: "red",
        greet: "こんにちは!"
    });

    // ヘルパーをJsRenderに登録する場合(2)
    //
    // $.views.helpersで登録した同名のヘルパーメソッド、プロパティは
    // 後勝ちとなる
    $.views.helpers("greet", "Hello !");


    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"に変数itemsとヘルパーを渡し、
    // レンダリング結果を表示
    $('#result').html($.render.linkTemplate(items, helper));
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
    <p>
        <span>{{>~greet}}</span>
        <span class="{{>~color}}">{{:name}}</span>が{{:num}}{{:~unit(#data)}}あります
    </p>
</script>
</body>
</html>

説明

JsRenderチュートリアル - テンプレートに渡したオブジェクトのメソッドを呼び出す - Do You PHP はてなではレンダリングする際にメソッドを含むデータをテンプレートに渡しましたが、JsRenderではヘルパーと呼ばれるオブジェクトを第2引数に渡すこともできます。

このヘルパーメソッドプロパティを定義しておくと、テンプレート内から呼び出し・参照することができます。

この場合、"~"(チルダ)を使ってメソッド名やプロパティ名を指定します。

~[メソッド名]([引数], ...)
~[プロパティ名]

また、ヘルパーをJsRender自身に登録することもできます。この場合の構文は以下のようになります。

$.views.helpers([ヘルパー名], [JavaScriptの関数やプロパティの値]);
  もしくは
$.views.helpers([プロパティやメソッドを含むオブジェクト]);

コード例ではJsRenderにヘルパーを2回登録し、またrender時にもヘルパーを渡していますが、この場合に同名のメソッドプロパティがあった場合は、それぞれ

render時に渡されたヘルパー > JsRenderに登録したヘルパー(2) > JsRenderに登録したヘルパー(1)

という具合に上書きされます。

JSFiddleで動作を見る

参照