2014年07月17日

Knockout-es5でrateLimitを設定する

Knockout-ES5はKnockout.jsをES5のgetter/setterに対応してくれる便利なプラグイン。ただrateLimitなどの標準の機能を利用しようとするとちょっと工夫が必要。

ko.track(this);
ko.getObservable(this, 'fuga').extend({ rateLimit: 100 });


ko.track()した後にko.getObservable()でrateLimitを設定したいオブジェクトを取得してからextendで機能拡張を行うとOK。
posted by ねこまんま at 15:13 | Comment(0) | TrackBack(0) | Knockout.js | 更新情報をチェックする

2014年07月09日

Knockout入門2

Knockout入門1の続き、Knockout : Computed Observablesの内容を確認していく。

以下のようにAppViewModelコンストラクタを使ってインスタンスをko.applyBindings()で監視。

<span data-bind="text: firstName"></span>
<span data-bind="text: lastName"></span>
<script>
function AppViewModel() {
this.firstName = ko.observable('Bob');
this.lastName = ko.observable('Smith');
}
ko.applyBindings(new AppViewModel());
</script>


以下のようにko.computed()を使ってfirstNameとlastNameからfullNameというプロパティを作成する。

function AppViewModel() {
this.firstName = ko.observable('Bob');
this.lastName = ko.observable('Smith');
this.fullName = ko.computed(function() {
return this.firstName() + " " + this.lastName();
}, this);
}
ko.applyBindings(new AppViewModel());


次のようにthisを変数selfの中に入れておいて参照することも可能。

function AppViewModel() {
var self = this;
self.firstName = ko.observable('Bob');
self.lastName = ko.observable('Smith');
self.fullName = ko.computed(function() {
return self.firstName() + " " + self.lastName();
});
}
ko.applyBindings(new AppViewModel());


次のように指定することも可能。

function AppViewModel() {
this.firstName = ko.observable('Bob');
this.lastName = ko.observable('Smith');
}
var myViewModel = new AppViewModel()
myViewModel.fullName = ko.computed(function() {
return myViewModel.firstName() + " " + myViewModel.lastName();
})
ko.applyBindings(myViewModel);
myViewModel.firstName("fuga");


このままではfullName()はあくまで読み取り専用である。以下のようにすることで書き込みも可能になる。
(lastIndexOf()で半角スペースがあるか判定してsubstring()でその前後を抽出)

function MyViewModel() {
this.firstName = ko.observable('Planet');
this.lastName = ko.observable('Earth');
this.fullName = ko.computed({
read: function () {
return this.firstName() + " " + this.lastName();
},
write: function (value) {
var lastSpacePos = value.lastIndexOf(" ");
if (lastSpacePos > 0) { // Ignore values with no space character
this.firstName(value.substring(0, lastSpacePos)); // Update "firstName"
this.lastName(value.substring(lastSpacePos + 1)); // Update "lastName"
}
},
owner: this
});
}
var myViewModel = new MyViewModel();
ko.applyBindings(myViewModel);
myViewModel.fullName("hoge fuga");


ここからは別サンプル。formattedPriceは数値を$123.00といったフォーマットに整形して表示する機能。toFixed()で少数表示に対応している。priceプロパティにはあくまで数値が格納されているがformattedPriceでそれを$表記に変更して出力している。

<p>Enter bid price: <input data-bind="value: formattedPrice"/></p>
<script>
function MyViewModel() {
this.price = ko.observable(25.99);
this.formattedPrice = ko.computed({
read: function () {
return '$' + this.price().toFixed(2);
},
write: function (value) {
// Strip out unwanted characters, parse as float, then write the raw data back to the underlying "price" observable
value = parseFloat(value.replace(/[^\.\d]/g, ""));
this.price(isNaN(value) ? 0 : value); // Write to underlying storage
},
owner: this
});
}
ko.applyBindings(new MyViewModel());
</script>


次のサンプルは入力フォーム(attemptedValue)の値が数値の場合はlastInputWasValid()がfalseをそうでない場合あtrueを返す

<p>Enter a numeric value: <input data-bind="value: attemptedValue"/></p>
<div data-bind="visible: !lastInputWasValid()">That's not a number!</div>
<script>
function MyViewModel() {
this.acceptedNumericValue = ko.observable(123);
this.lastInputWasValid = ko.observable(true);
this.attemptedValue = ko.computed({
read: this.acceptedNumericValue,
write: function (value) {
if (isNaN(value))
this.lastInputWasValid(false);
else {
this.lastInputWasValid(true);
this.acceptedNumericValue(value); // Write to underlying storage
}
},
owner: this
});
}
ko.applyBindings(new MyViewModel());
</script>
タグ:KNOCKOUT
posted by ねこまんま at 06:38 | Comment(0) | TrackBack(0) | Knockout.js | 更新情報をチェックする

Knockout入門1

Knockoutをお仕事で使うのでメモ。

Knockout入門1ではKnockout : Observablesを読みながら基本機能の確認。

まず、以下のようにCDNからサクッと読み込める

<script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-3.0.0.js"></script>


最新の3.1は以下のCDNで配布されている。

<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout/3.1.0/knockout-min.js"></script>


まずは基本のko.applyBindings()を使ってみる。ko.applyBindings()はオブジェクトの内容をDOMに反映させるメソッド。

<span data-bind="text: personName"></span>
<span data-bind="text: personAge"></span>
<script>
var myViewModel = {
personName: 'Bob',
personAge: 123
};
ko.applyBindings(myViewModel);
</script>


上記のように記述するとspanの内容が以下のように書き換えられる。

<span data-bind="text: personName">Bob</span>
<span data-bind="text: personAge">123</span>


つぎにデータバインディングの醍醐味であるko.observable()。上記のスクリプトを下記のように変更。

最初は画面にBobと表示される。myViewModel.personName()のように引数に何も入れなければプロパティの取得が可能で、myViewModel.personName("Tomy")のように引数に値を入れればプロパティの内容の変更が可能。変更を行うと即座にHTML上に反映させられる。

var myViewModel = {
personName: ko.observable('Bob'),
personAge: ko.observable('123')
};
ko.applyBindings(myViewModel);
alert(myViewModel.personName());
myViewModel.personName("Tomy");


subscribe()で変更をscript側で感知することも可能

var myViewModel = {
personName: ko.observable('Bob'),
personAge: ko.observable('123')
};
ko.applyBindings(myViewModel);
myViewModel.personName.subscribe(function(newValue) {
alert("The person's new name is " + newValue);
});
myViewModel.personName("Tomy");


変更直前にスクリプトを実行したい場合は以下のように第3引数に"beforeChange"を記述する。

var myViewModel = {
personName: ko.observable('Bob'),
personAge: ko.observable('123')
};
ko.applyBindings(myViewModel);
myViewModel.personName.subscribe(function(oldValue) {
alert("The person's previous name is " + oldValue);
}, null, "beforeChange");
myViewModel.personName("Tomy");


ちなみに第2引数にはコールバック関数内のthisで利用するオブジェクトが指定できる。以下のように指定をすると「hogeBob」とアラートが表示される。

var myViewModel = {
personName: ko.observable('Bob'),
personAge: ko.observable('123')
};
ko.applyBindings(myViewModel);
myViewModel.personName.subscribe(function(oldValue) {
alert(this.fuga + oldValue);
}, {"fuga":"hoge"}, "beforeChange");
myViewModel.personName("Tomy");


バインドしたsubscribeイベントはdispose();で破棄することができる。"Tomy1"への変更の時点ではアラートがでるが"Tomy2"への変更ではアラートが表示されない。

var myViewModel = {
personName: ko.observable('Bob'),
personAge: ko.observable('123')
};
ko.applyBindings(myViewModel);
var subscription = myViewModel.personName.subscribe(function(newValue) {
alert("The person's new name is " + newValue);
});
myViewModel.personName("Tomy1");
subscription.dispose();
myViewModel.personName("Tomy2");


subscribeイベントは基本的には変更があった場合に発火する。たとえば次のケースではpersonNameをBob→Bobに変更しているので変更がないとみなされてアラートは表示されない。

var myViewModel = {
personName: ko.observable('Bob'),
personAge: ko.observable('123')
};
ko.applyBindings(myViewModel);
var subscription = myViewModel.personName.subscribe(function(newValue) {
alert("The person's new name is " + newValue);
});
myViewModel.personName("Bob");


ただし、この挙動はextend({ notify: 'always' })を指定することで変更でき、同じ値でもsubscribeイベントを発火させることができる。

var myViewModel = {
personName: ko.observable('Bob'),
personAge: ko.observable('123')
};
ko.applyBindings(myViewModel);
myViewModel.personName.extend({ notify: 'always' });
var subscription = myViewModel.personName.subscribe(function(newValue) {
alert("The person's new name is " + newValue);
});
myViewModel.personName("Bob");


他にも、extend({ rateLimit: Number })などがある。これは変更のタイミングを制御するための機能です(Knockout 3.1より追加)。

以下のように記述するとsubscribeイベントは4回発火する。

var myViewModel = {
personName: ko.observable('Bob'),
personAge: ko.observable('123')
};
ko.applyBindings(myViewModel);
var subscription = myViewModel.personName.subscribe(function(newValue) {
alert("The person's new name is " + newValue);
});
myViewModel.personName("Bob1");
myViewModel.personName("Bob2");
myViewModel.personName("Bob3");
myViewModel.personName("Bob4");


これを以下のようにextend({ rateLimit: 50 })を追加すると変更のタイミングは50ms秒後になり、アラートは「Bob4」のみが表示されるようになる。

var myViewModel = {
personName: ko.observable('Bob'),
personAge: ko.observable('123')
};
ko.applyBindings(myViewModel);
var subscription = myViewModel.personName.subscribe(function(newValue) {
alert("The person's new name is " + newValue);
});
myViewModel.personName.extend({ rateLimit: 50 });
myViewModel.personName("Bob1");
myViewModel.personName("Bob2");
myViewModel.personName("Bob3");
myViewModel.personName("Bob4");


ひとまず、Knockout入門1はこんなかんじでKnockout入門2に続く。
タグ:KNOCKOUT
posted by ねこまんま at 06:05 | Comment(0) | TrackBack(0) | Knockout.js | 更新情報をチェックする

2014年03月15日

Knockout.jsでタブパネル2

Knockout.jsでタブパネルで気になった点を修正。



<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<style>
button.active{
font-weight:bold;
}
</style>
<div data-bind="click: tabChange">
<a href="#tab1" data-bind="css: { active: tabCheck($element) }">tab1</a>
<a href="#tab2" data-bind="css: { active: tabCheck($element) }">tab2</a>
<a href="#tab3" data-bind="css: { active: tabCheck($element) }">tab3</a>
</div>
<div>
<div id="tab1" data-bind="visible:panelCheck($element)">CSS1</div>
<div id="tab2" data-bind="visible:panelCheck($element)">CSS2</div>
<div id="tab3" data-bind="visible:panelCheck($element)">CSS3</div>
</div>
<script src="knockout-3.1.0.js"></script>
<script>
//ViewModelを定義
var tabPanelViewModel = function(){
//Model作成
this.selectTab = ko.observable("#tab1");
}
//Modelチェンジ
tabPanelViewModel.prototype.tabChange = function(o,e){
if(e.target.tagName=="A"){
this.selectTab(e.target.getAttribute("href"));
}
}
//タブチェンジ
tabPanelViewModel.prototype.tabCheck = function(e){
return this.selectTab()== e.getAttribute("href");
}
//パネルチェンジ
tabPanelViewModel.prototype.panelCheck = function(e){
return this.selectTab()=="#"+e.getAttribute("id");
}
//bindingスタート
ko.applyBindings( new tabPanelViewModel() );
</script>
</body>
</html>


clickイベントのバインドがめんどくさいのでdelegateで実装。タブやパネルの変化も別メソッドで実装することでviewの記述やメンテナンス性を向上させている。
タグ:Knockout.js
posted by ねこまんま at 11:24 | Comment(0) | TrackBack(0) | Knockout.js | 更新情報をチェックする

2014年03月13日

Knockout.jsでタブパネル

Knockout.jsを使って簡単なタブパネルを作ってみた - E-riverstyle Vanguardを参考にKnockout.jsでタブパネルを作ってみた



<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<style>
button.active{
font-weight:bold;
}
</style>
<button data-bind="click: tabChange.bind($data,0),css: { active: selectTab() == 0 }">button</button>
<button data-bind="click: tabChange.bind($data,1),css: { active: selectTab() == 1 }">button</button>
<button data-bind="click: tabChange.bind($data,2),css: { active: selectTab() == 2 }">button</button>
<div data-bind="visible:selectTab()==0">CSS1</div>
<div data-bind="visible:selectTab()==1">CSS2</div>
<div data-bind="visible:selectTab()==2">CSS3</div>
<script src="knockout-3.1.0.js"></script>
<script>
//ViewModelを定義
function TabplanelModel(){
var self = this;
self.selectTab = ko.observable(0);
self.tabChange = function(n){
self.selectTab(n);
}
}
//bindingスタート
ko.applyBindings( new TabplanelModel() );
</script>
</body>
</html>


当初イベントのバインドは以下のように書いてたけど匿名関数で書くのめんどくさいなと思ったらbind()で行けるっぽい。

<button data-bind="click: function(){ tabChange(0) },css: { active: selectTab() == 0 }">button</button>


課題としてviewにvisible:selectTab()==2といった条件が複数あって汎用性が低いなとおもってるのでそこを改善したい
posted by ねこまんま at 11:15 | Comment(0) | TrackBack(0) | Knockout.js | 更新情報をチェックする

2014年03月11日

Knockout.js入門

必要に迫られてAngularJSそっちのけでKnockout.jsの勉強

Hello Worldは「knockout.jsでさくさくWebアプリ開発|1 pixel|サイバーエージェント公式クリエイターズブログ」がよかった。

つくったのは以下のようなサンプル。

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<p><input type="text" data-bind="value: message, valueUpdate: 'afterkeydown'"></p>
<p id="result" data-bind="text: message">ここに入力されたテキストと同じものが入る</p>
<script src="knockout-3.1.0.js"></script>
<script>
//ViewModelを定義
function TestappViewModel(){
var self = this;
self.message = ko.observable('');
}
//bindingスタート
ko.applyBindings( new TestappViewModel() );
</script>
</body>
</html>


data-bind属性でmodelとviewを関連付けられる。

data-bind="value: message"とするとinput要素やtextarea要素のvalueをdata-bind="value: text"とすると要素のinnerTextがが関連付けられる。

data-bind="visible:message"などとするとmodelに値がある場合のみ表示。

<button data-bind="enable: message">button</button>などのようにenableでdisable属性の制御ができる。
posted by ねこまんま at 15:10 | Comment(0) | TrackBack(0) | Knockout.js | 更新情報をチェックする