主にプログラミングに関して。Python, .NET Framework(C#), JavaScript, その他いくらか。
記事にあるサンプルやコードは要検証。使用に際しては責任を負いかねます

スポンサーサイト

                
tags:
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

JavaScript: knockout.jsとMVVMパターンってなんぞ?

                
http://knockoutjs.com/index.html

 アプリケーションをつくる概念として、ModelやViewという言葉がある。Viewにはユーザが触れるデータが表示されている。テキストボックスやボタン、プルダウンなど様々な形で。このデータをユーザが選択して変更した際に必要に応じて、新しい要素を追加表示する、すでにある要素を置換して別のものを表示するなどの処理を実行して目的を達していこうとする。たとえばコンサートのチケットを予約したいとき客には、日時を選択→席のタイプを選択→枚数を選択、という手順を踏ませたい。こちらはそれらの選択の結果を受け取って最終的なチケット代金を提示する。このViewの裏側でおこなわれる一連の手続きがModelである。そしえViewとModelの橋渡しをするのがViewModelの役目ということだ。View, Mode, ViewModelの三つを合わせてMVVMパターン。これはUI作りのために近年出てきた技術概念らしい。そしてJavaScriptでそれをやるのがKnockoutという外部ライブラリ。
 モデルが変わったら即座にUIにそれを反映させるとかってメリットがあるらしい。まあこんな理屈によるメリット紹介を読むのはほどほどに、スクリプトと動作によってどう素晴らしいか確認する。
http://knockoutjs.com/examples/helloWorld.html

…最初のデモに触れてみたけどとくにすごいとか感じない。クライアントの入力を即座にUIに反映させるとか、要素のフォーカスの状態が変わったときに動くイベントを仕込んでおけばいいじゃない。jQueryで同様に動作するものを書いても大した容量の違いは生まれない。
<p>ファーストネーム: <input data-bind="value: firstName" /></p>
<p>ラストネーム: <input data-bind="value: lastName" /></p>
<h2>Hello, <span data-bind="text: fullName"> </span>!</h2>
<script>
var ViewModel = function(first, last) {
this.firstName = ko.observable(first);
this.lastName = ko.observable(last);

this.fullName = ko.computed(function() {
return this.firstName() + " " + this.lastName();
}, this);
};

ko.applyBindings(new ViewModel("Planet", "Earth"));
</script>

上記がknockout、下記がjQuery
<p>ファーストネーム: <input id="first" /></p>
<p>ラストネーム: <input id="last" /></p>
<h2>Hello, <span id="show"> </span>!</h2>
<script>
function init(first, last)
{
function inputForm()
{
$("#show").text($("#first").val() + " " + $("#last").val());
}

$("#first").val(first).change(inputForm);

$("#last").val(last).change(inputForm);

inputForm();
}

init("Planet", "Earth");
</script>


これじゃなんとも言えない。もっといいデモがあるんじゃないかと考えて次のデモへ。
http://knockoutjs.com/examples/clickCounter.html
クリックカウンターだ。クリックが任意の回数に達すると、ボタンがdisableになって新しい要素が表示される。これと同じ動作をするものをjQueryを使ってまた書いてみる。
<div>You've clicked <span id='num'>&nbsp;</span> times</div>
<button id='countButton'>Click me</button>
<div id='caution'>
That's too many clicks! Please stop before you wear out your fingers.
<button id='resetButton'>Reset clicks</button>
</div>
<script>
function ClickCounter()
{
var numberofClicks = 0;
$("#num").text(numberofClicks);
$("#caution").hide();

function hasClickedTooManyTimes()
{
$("#countButton").attr('disabled', true);
$("#caution").show();
}

function resetClicks()
{
numberofClicks = 0;
$("#num").text(numberofClicks);
$("#countButton").attr('disabled', false);
$("#caution").hide();
}

function registerClick()
{
$("#num").text(++numberofClicks);
if (numberofClicks >= 5)
{
hasClickedTooManyTimes();
}
}

$("#countButton").click(registerClick);
$("#resetButton").click(resetClicks);

}

ClickCounter();</script>

……けっこう面倒だった。クリックした回数をクロージャで保持しておいて、クリックに応じてその値とクリック上限回数を比較して、条件でクリックボタンをdisableにして、隠しておいたdiv要素を表示する。隠しておいたdiv要素のボタンがクリックされたらクリック回数をリセットして、先程のクリックボタンのdisableをキャンセルする。手続きが少し込み入ってきた。
 ここでknockoutデモのスクリプトを見てみる。
<div>You've clicked <span data-bind='text: numberOfClicks'>&nbsp;</span> times</div>

<button data-bind='click: registerClick, disable: hasClickedTooManyTimes'>Click me</button>

<div data-bind='visible: hasClickedTooManyTimes'>
That's too many clicks! Please stop before you wear out your fingers.
<button data-bind='click: resetClicks'>Reset clicks</button>
</div>

<script>View modelvar ClickCounterViewModel = function() {
// 変更が監視される変数numberofClicksを初期値0で宣言
this.numberOfClicks = ko.observable(0);

// カウンターボタンクリック時に実行される関数を宣言
// numberofClicksを更新。1プラス
this.registerClick = function() {
this.numberOfClicks(this.numberOfClicks() + 1);
};

// リセットボタンをクリック時に実行される関数を宣言
// numberofClicksの値を0にリセット
this.resetClicks = function() {
this.numberOfClicks(0);
};

// hennsuu numberofClicksが変更されたときに判定されるBool値を宣言
this.hasClickedTooManyTimes = ko.computed(function() {
return this.numberOfClicks() >= 3;
}, this);
};

ko.applyBindings(new ClickCounterViewModel());
</script>

 手続きの記述がけっこう楽になった。jQueryでは事細かに動作ロジックを記述していた。クリックされたらカウンターをインクリメントして、その値で判定をおこなって、DOMにアクセスして表示を書き換えてというふうに。
 knockoutを使った場合、クリックによるインクリメントがあったら、あらかじめ定義しておいたカウンターに値が依存している変数がすべて自動で更新され、その更新の結果がDOMにアクセスする関数を書かなくても表示に反映される。便利。

 従来メジャーであったのMVCパターンではModelとViewのあいだをControllerが取り持つ。Contorollerがイベントをキャッチして、それによってさまざまな更新が行われる。
 近年出てきたMVVMパターンの場合、どの変数を監視対象にするか決めればその変数に依存する変数ひいてはViewでの表示までが自動更新されるようになっており、イベントによって動作する関数を書く必要がなくなる。knockoutで行われる自動更新はなにか特別なAPIを使っているわけではなく、内部では要素へのフォーカス状態が変わったときに自動更新が働くようにしているだけではあるが、そのおかげで従来のGUIアプリケーションとは違った書き方が可能になる。これは便利であるし面白い。

 ためしに一つ書いてみた。インプットフォームを表示しておいて、入力された年齢によって表示するものを変えるというものである。やはりDOM操作を自分で書く必要がない。
13101300.html
How old are you?<br>
<input data-bind='value: age'>&nbsp;</span> years old
<br><br>

<div data-bind='visible: younger'>
Too young to see our contents!
</div>

<div data-bind='visible: adult'>
Enjoy our contents...<br>
*** some pictures here ***
</div>

<div data-bind='visible: older'>
Have a good life!
</div>

<script>
var AgeCounterViewModel = function() {
this.age = ko.observable(null);

this.younger = ko.computed(function() {
return (1 <= this.age() && this.age() < 20);
}, this);

this.adult = ko.computed(function() {
return (20 <= this.age() && this.age() < 100);
}, this);

this.older = ko.computed(function() {
return 100 <= this.age();
}, this);
};

ko.applyBindings(new AgeCounterViewModel());
</script>


 knockoutおよびそれを使ったMVVMパターンでのGUI実装の導入は、方法やメリットがおおよそつかめてきたのでここまでにしておく。ただ公式デモがまだ残っているので、それを試しながらknockoutの掘り下げを続ける。
 なお、jQueryのホスティングサービスをGoogleがやっているが、これと同様にknockoutもホスティングやってるかとGoogleをのぞいてみたがやっていなかった。代わりというか、Microsoftがホスティングサービスをおこなっていた。まあMicrosoftの中から出てきたものだしそうなるのかな。
http://www.asp.net/ajaxlibrary/CDN.ashx#Knockout_Releases_on_the_CDN_11
            

コメントの投稿

非公開コメント

プロフィール

hMatoba

Author:hMatoba
Github

最新記事
リンク
作ったものなど
月別アーカイブ
カテゴリ
タグリスト

検索フォーム
Amazon
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。