しょぼちむをExcel方眼紙で表現してみる
これはしょぼちむアドベントカレンダー23日目の記事です。
しょぼちむ Advent Calendar 2014 - Adventar
昨日は、ばふぁさんの「しょぼちむ!スクラムチームの外側の話をちょっと書いてみたよ!」でした!
今日は特にこれといって決めてないので、しょぼちむをExcel方眼紙にしようと思います。
以下の手順で行います。
以上です。
画像のリサイズおよび2値化には OpenCVを使います。
OpenCVのインストール(Mac)はこちらを参考にしました。
Mac - OS X に OpenCV を Homebrew で入れて python で動画入出力をする - Qiita
では早速ソースコードです。
まず vim を起動します。
$ vim syobochim.py
import cv2 img = cv2.imread('syobochim.jpg', cv2.IMREAD_GRAYSCALE) # 2. 画像を適当に小さくする(リサイズ) thumb = cv2.resize(img,(160, 128)) # 3. 画像を2値化する thresh = 120 img_bin = cv2.threshold(thumb, thresh, 255, cv2.THRESH_BINARY)[1] # 4. 画像を 0 と 1 でマッピングして文字列を取り出す for y in range(0, 127): for x in range(0, 159): px = img_bin[y, x] if px != 0: print '1', else: print '0', print ''
そしてこれを
( ^ω^)
⊃ syobochim.py ⊂
こうして…
( ^ω^)
≡⊃⊂≡
$ python syobochim.py > syobochim.txt
こうじゃ
( ^ω^)
⊃ Excel方眼紙 ⊂
本当はここから Arduinoにシリアル通信で画像を送り、LCDディスプレイで表示しようと思ったんだけど、送信データ量が多くて送れませんでした。
バイナリデータで送ろうとしたけどpySerialでは文字列でしか送れないことと、Arduinoは128byteのread bufferしかないのが原因っぽい。多分頑張ればできるはず。
とりあえず時間があったら後でやろうかなと思うので上記ソースをちょっと修正したPythonのソースコードを備忘録で残しておく。
※動いたので追記
Arduinoのバッファを消費するタイミングをうまくスリープで調整すればできた。
けどスリープのタイミングがなかなか難しいし遅い。
バッファサイズをみてやればいいような気がするけどこのへんの知識ないので難しい・・。
# convert image to 160 x 128 pixel resolution. import array import cv2 import serial import time # resize img = cv2.imread('syobochim.jpg', cv2.IMREAD_GRAYSCALE) thumb = cv2.resize(img,(160, 128)) # binary thresh = 120 img_bin = cv2.threshold(thumb, thresh, 255, cv2.THRESH_BINARY)[1] # serial ser = serial.Serial() ser.port = '/dev/tty.usbmodemfa131' ser.baudrate = 9600 ser.open() time.sleep(5) for y in range(0, 128): data = "" for x in range(0, 160): px = img_bin[y, x] if px != 0: data += "1" else: data += "0" ret = ser.write(data) time.sleep(0.5) time.sleep(1) ser.close()
#include <SPI.h> #include <SD.h> #include <TFT.h> // Arduino LCD library // pin definition for the Uno #define sd_cs 4 #define lcd_cs 10 #define dc 9 #define rst 8 TFT tft = TFT(lcd_cs, dc, rst); int x,y = 0; int rcv = 0; void setup() { tft.begin(); tft.background(0, 0, 0); Serial.begin(9600); } void loop() { while((rcv = Serial.available()) > 0){ for(int i=0; i<rcv; i++) { int val = Serial.read(); if (val == 48) { tft.drawPixel(x, y, 0x0000); x++; } else if(val == 49){ tft.drawPixel(x, y, 0xFFFF); // 0xFFFF x++; } if(x>=160) { x = 0; y++; } } } }
KnockoutでHandlebarsを使ってみる
これはKnockoutJSアドベントカレンダー22日目の記事です。
KnockoutJS Advent Calendar 2014 - Qiita
Knockoutでは外部のテンプレートエンジンを組み込んで使うことができます。
組み込み方はソースコード内に記載してあります。
knockout/templateEngine.js at master · knockout/knockout · GitHub
要約(ちょっと後半の訳があやしい)
// 独自のテンプレートエンジンを作りたい場合は次のようにしてください。 // // [1] ko.nativeTemplateEngineと同じようにこのクラスを継承します。 // [2] 'renderTemplateSource' をオーバーライドし、次のシグネチャのメソッドを実装します。 // // function (templateSource, bindingContext, options) { // // - templateSource.text() はテンプレートでレンダリングするテキストです // // - bindingContext.$data はテンプレートに渡すデータです // // - 他にも bindingContext.$parent, bindingContext.$parents, bindingContext.$root がテンプレートで使えます // // - options は "data-bind: { template: options }" で与えられたプロパティにアクセスすることができます // // - templateDocument はテンプレートの documentオブジェクトです // // // // Return value: DOM要素の配列 // } // // [3] 'createJavaScriptEvaluatorBlock' をオーバーライドし、 次のシグネチャのメソッドを実装します。 // // function (script) { // // Return value: Whatever syntax means "Evaluate the JavaScript statement 'script' and output the result" // // 'script' を評価して結果を出力します。 // // 例えば、 jquery.tmpl テンプレートエンジンは 'someScript' を '${someScript} に変換します // } // // This is only necessary if you want to allow data-bind attributes to reference arbitrary template variables. // これはあなたがデータバインドの属性を任意のテンプレート変数が参照する場合にのみ必要です。 // もしこれを許可したくない場合は 'allowTemplateRewriting' プロパティを false にします(ko.nativeTemplateEngineと同じように) // その場合は 'createJavaScriptEvaluatorBlock' をオーバーライドする必要はありません。
ko.templateSourcesあたりのソースコードに renderTemplateSource の引数の templateSource の詳しい内容が書かれています。
というわけで KnockoutJS から Handlebars を使ってみましょう。
公式ドキュメントの template バインディングの一番下にUnderscoreのサンプルがあるのでこれを真似てみます。
Knockout : The "template" binding
ソースはこんな感じ。
<div data-bind="foreach: people"> <div data-bind="template: { name: 'person-template' }"></div> <hr> </div> <script type="text/html" id="person-template"> <table> <tr> <th>Name</th> <td>{{name}}</td> </tr> <tr> <th>Gender</th> <td>{{gender}}</td> </tr> <tr> <th>Profile</th> <td> {{#each profiles}} <span>{{@index}} : {{{this}}}</span> {{/each}} </td> </tr> {{#if country}} <tr> <th>Country</th> <td>{{country}}</td> </tr> {{/if}} </table> </script> <script type="text/javascript"> ko.handlebarsTemplateEngine = function () { } ko.handlebarsTemplateEngine.prototype = ko.utils.extend(new ko.templateEngine(), { renderTemplateSource: function (templateSource, bindingContext, options) { var precompiled = templateSource['data']('precompiled'); if (!precompiled) { precompiled = Handlebars.compile(templateSource.text()); } var renderedMarkup = precompiled(bindingContext.$data).replace(/\s+/g, " "); return ko.utils.parseHtmlFragment(renderedMarkup); }, allowTemplateRewriting: false, createJavaScriptEvaluatorBlock: function(script) { return "{{" + script + "}}"; } }); ko.setTemplateEngine(new ko.handlebarsTemplateEngine()); function Person(name, gender, profiles, country){ this.name = name; this.gender = gender; this.profiles = profiles; if(country) { this.country = country; } } function ViewModel() { this.people = ko.observableArray([ new Person("Bob", "Male", ["Hello", "World"]), new Person("Mike", "Male", ["Foo"]), new Person("Katherine", "Female", []), new Person("Ken", "Male", [], "Japan"), ]); } ko.applyBindings(new ViewModel()); </script>
jsfiddleで見れます。
http://jsfiddle.net/vjj309sz/
明日は @motoyan_k さんの 「Knockout.hxについて」です。
よろしくおねがいします!
Mappingプラグイン(その2)
これはKnockoutJSアドベントカレンダー19日目の記事です。
KnockoutJS Advent Calendar 2014 - Qiita
MappingプラグインはKnockoutJS公式のドキュメントに唯一書かれてあるプラグインです。
http://knockoutjs.com/documentation/plugins-mapping.html
公式ドキュメントには色々書いてありますが、簡単に言うと
「JSのオブジェクト内のプロパティや配列を observable / observableArray に変換」するプラグインです。
前回基本的な使い方を説明したのですが、それだけでは実際に使うのは難しい場面が多いと思うので、
その際に必要になるマッピングオプションを説明しようと思います。
公式ドキュメントを要約した感じなので詳しくはそちらを参照してください。
key, create, update
以下のようなデータがあります。
var data = { name: 'Scot', children: [ { id : 1, name : 'Alicw' } ] } var viewModel = ko.mapping.fromJS(data);
上記のコードにtypoがあるので、以下のコードで上書きをしたいと思いました。
var data = { name: 'Scott', children: [ { id : 1, name : 'Alice' } ] } ko.mapping.fromJS(data, viewModel);
上記のように書くことで name は 'Scott' に変わりましたが、 children は上書きしてほしかったのですが、
上書きされずに一度削除され新しく追加されました。
と公式ドキュメントにはこう書いてあるのですが、挙動が分かりにくいかと思うので
マッピングオプションの create と update も加えてサンプルを使って説明します。
まず先程のサンプルの挙動がどうなるのかを見てみます。
サンプルコード
var data = { name: 'Scot', children: [ { id : 1, name : 'Alicw' } ] } var viewModel = ko.mapping.fromJS(data); var data = { name: 'Scott', children: [ { id : 1, name : 'Alice' } ] } var mapping = { 'children': { create: function(options) { console.log("create"); return options.data; }, update: function(options) { console.log("update"); return options.data; } } } ko.mapping.fromJS(data, mapping, viewModel); ko.applyBindings(viewModel);
ログ出力は以下のようになりました。
create update
これを id を key にして上書きするようにしてみましょう。
<p data-bind="text: name"></p> <div data-bind="foreach:children"> <p data-bind="text: id"></p> <p data-bind="text: name"></p> </div> <script> var data = { name: 'Scot', children: [ { id : 1, name : 'Alicw' } ] } var viewModel = ko.mapping.fromJS(data); var data = { name: 'Scott', children: [ { id : 1, name : 'Alice' } ] } var mapping = { 'children': { key: function(data) { return ko.utils.unwrapObservable(data.id); }, create: function(options) { console.log("create"); return options.data; }, update: function(options) { console.log("update"); return {id:options.data.id, name:options.data.name}; } } } ko.mapping.fromJS(data, mapping, viewModel); ko.applyBindings(viewModel); </script>
するとログ出力は以下のようになります。
update
上記のように key を指定することで create を実行せず key の情報をもとに update だけを実行します。
mappedRemove, mappedCreate
公式ドキュメントにありますが、どういう出力結果になるかぱっと見で分からなかったので試してみました。
var obj = [ { id : 1 }, { id : 2 } ] var result = ko.mapping.fromJS(obj, { key: function(item) { return ko.utils.unwrapObservable(item.id); }, create: function(options) { console.log("create"); return options.data; }, update: function(options) { console.log("update"); return options.data; }, }); console.log(result().length); // 配列の長さ: 2 console.log(result()[0].id); // 出力結果: 1 console.log(result()[1].id); // 出力結果: 2 result.mappedRemove({ id : 1 }); console.log(result().length); // 配列の長さ: 1 console.log(result()[0].id); // 出力結果: 2 var newItem = result.mappedCreate({ id : 3 }); console.log(result().length); // 配列の長さ: 2 console.log(result()[0].id); // 出力結果: 2 console.log(result()[1].id); // 出力結果: 3 console.log(newItem); // 出力結果: Object {id: 3}
他にも ignore, include, copy, observe や mappedXxx が色々ありますがまた機会があれば。
明日は @yusuke_nozoe さんです!よろしくお願いします!
Mappingプラグイン(その1)
これはKnockoutJSアドベントカレンダー17日目の記事です。
KnockoutJS Advent Calendar 2014 - Qiita
MappingプラグインはKnockoutJS公式のドキュメントに唯一書かれてあるプラグインです。
http://knockoutjs.com/documentation/plugins-mapping.html
公式ドキュメントには色々書いてありますが、簡単に言うと
「JSのオブジェクト内のプロパティや配列を observable / observableArray に変換」するプラグインです。
個人的にはばっちりハマるパターンにまだ落とし込めてないので良さげなパターンあれば教えて欲しいです。
下図の Model 部分についてサーバからJSONを取得してマッピングさせるときに便利なんじゃないかと思います。
※公式ドキュメントでは view model って言っているんですが observable / observableArray だけのViewModelって
あんま使い途なさそうでその辺の説明の意図がよく分かっていません。
http://www.wisetechglobal.com/Portals/WTG/downloads/WTF04-Slides/index.html#11
では最初にAPIを見ていきます。
基本的に使うものは以下の4つです。
(1) ko.maping.fromJS
(2) ko.mapping.toJS
(3) ko.mapping.fromJSON
(4) ko.mapping.toJSON
(1) は JSオブジェクト内のプロパティや配列を observable / observableArray に変換します。
実際やってみると以下のような感じになります。
var data = { first: 'Hello', last: 'World', name: ['h','e','l','l','o'] }; var model = ko.mapping.fromJS(data); console.log(model.first); console.log(model.first()); console.log(model.first.peek()); console.log(model.name); console.log(model.name()); console.log(model.name.peek());
出力結果
function d(){if(0<arguments.length)return d.Pa(c,arguments[0])&&(d.X(),c=arguments[0],d.W()),this;a.k.Jb(d);return c} Hello Hello function d(){if(0<arguments.length)return d.Pa(c,arguments[0])&&(d.X(),c=arguments[0],d.W()),this;a.k.Jb(d);return c} ["h", "e", "l", "l", "o"] ["h", "e", "l", "l", "o"]
(2) は逆にJSONオブジェクトに変換します。
こんな感じ。
function ViewModel(){ var self = this; self.first = ko.observable('Hello'); self.last = ko.observable('World'); self.name = ko.observableArray(['h','e','l','l','o']); } var jsObj = ko.mapping.toJS(new ViewModel()); console.log(jsObj);
出力結果
Object { first: "Hello", last: "World", name: Array[5] 0: "h" 1: "e" 2: "l" 3: "l" 4: "o" length: 5 __proto__: Array[0] __proto__: Object }
(3)と(4)はソース読むとすぐ分かるのですが、
(3) は ko.utils.parseJson() した結果を (1) に渡すだけ、
(4) は (2)の結果を ko.utils.stringifyJson() するだけになります。
次に (1) fromJSと (3) fromJSON メソッドの引数について説明します。
この2つのメソッドは引数の数と型によって動作が若干異なります。
・引数が1つの場合
データを引数にマッピングされたモデルを返します。
例)var viewModel = ko.mapping.fromJSON(data)
・引数が2つの場合
2つめの引数が ViewModel の場合、これを更新します。
例)ko.mapping.fromJSON(data,koMappingCreatedViewModel)
または2つめの引数をマッピングの際のオプションとして渡すことができます。
例)var viewModel = ko.mapping.fromJSON(data,options)
・引数が3つの場合
2つめの引数をオプション、3つめの引数をターゲットとして更新します。
例)ko.mapping.fromJSON(data, options, target)
基本的な使い方はこれだけなのですが、実際使おうとするとこれだけではなかなか難しいので、
以下のマッピング時のオプションが用意されています。
それらの使い方についてはまた次回。
明日は @sukobuto さんの「SPA で Enter キーフォーカス遷移」です!
よろしくおねがいします!
Knockoutの template バインディング
これはKnockoutJSアドベントカレンダー15日目の記事です。
KnockoutJS Advent Calendar 2014 - Qiita
KnockoutJSには template バインディングなるものがあります。
これは何かというと HTMLの一部を切り出してテンプレート化して使える機能です。
利用用途としては「Componentsにするほどでもないけどこの画面でよく使うので切り出しておきたい」と
いった場合などでしょうか。
それではさっそく公式サイトのドキュメントからかいつまんで説明していきたいと思います。
http://knockoutjs.com/documentation/template-binding.html
公式ドキュメントにあるように、 template バインディングの書き方は以下のようになります。
<h2>Participants</h2> Here are the participants: <div data-bind="template: { name: 'person-template', data: buyer }"></div> <div data-bind="template: { name: 'person-template', data: seller }"></div> <script type="text/html" id="person-template"> <h3 data-bind="text: name"></h3> <p>Credits: <span data-bind="text: credits"></span></p> </script> <script type="text/javascript"> function MyViewModel() { this.buyer = { name: 'Franklin', credits: 250 }; this.seller = { name: 'Mario', credits: 5800 }; } ko.applyBindings(new MyViewModel()); </script>
テンプレート化したい部分を で切り出して ID を振ります。
そしてその振ったIDを templateバインディングの name プロパティに指定してバインドさせます。
data プロパティにはテンプレートにレンダリングしたいオブジェクトを設定します。
このオブジェクトのプロパティがテンプレート内の変数として利用できます。
ドキュメントには data に値を設定しない場合は foreach のパラメータか現在のモデルオブジェクトを使用するとあります。
早速試してみましょう。dataを省いた以下のようなコードでもちゃんと動作しました。
<div data-bind="foreach: people"> <div data-bind="template: { name: 'person-template' }"></div> </div> <script type="text/html" id="person-template"> <h3 data-bind="text: name"></h3> <p><span data-bind="text: gender"></span></p> </script> <script type="text/javascript"> function Person(name, gender){ this.name = name; this.gender = gender; } function ViewModel() { this.people = ko.observableArray([ new Person("Bob", "Male"), new Person("Mike", "Male"), new Person("Katherine", "Female") ]); } ko.applyBindings(new ViewModel()); </script>
紹介した name, data プロパティ以外にもtemplateバインディングには
以下のようなプロパティが使えます。
if, foreach, as, afterRender, afterAdd, beforeRemove
if はプロパティにの値が true だったら表示、そうじゃなかったら非表示っていう感じです。
簡単なサンプル作ってみました。
http://jsfiddle.net/tan_go238/uyhtd1k7/
<h2>Participants</h2> Here are the participants:<br> <input type="checkbox" data-bind="checked: show1"> <br> <input type="checkbox" data-bind="checked: show2"> <br> <div data-bind="template: { name: 'person-template', data: buyer, if: show1 }"></div> <div data-bind="template: { name: 'person-template', data: seller, if: show2 }"></div> <script type="text/html" id="person-template"> <h3 data-bind="text: name"></h3> <p>Credits: <span data-bind="text: credits"></span></p> </script> <script type="text/javascript"> function MyViewModel() { this.show1 = ko.observable(); this.show2 = ko.observable(); this.buyer = { name: 'Franklin', credits: 250 }; this.seller = { name: 'Mario', credits: 5800 }; } ko.applyBindings(new MyViewModel()); </script>
foreach はオブジェクトの配列をぐるぐる回す感じで使います。
テンプレート内の変数になるのは(配列の中の)それぞれオブジェクトのプロパティです。
また "foreach: someExpression" は "template: { foreach: someExpression }" と同じになります。
使い方は公式サイトにサンプルがあるのでそれを参考にしてください。
foreachの中でこのようにテンプレートをネストすることもできます。
http://jsfiddle.net/tan_go238/1gwh5a45/1/
<div data-bind="template: { name: 'parent-template', foreach: people }"></div> <script type="text/html" id="parent-template"> <h3 data-bind="text: name"></h3> <div data-bind="template: { name: 'child-template', foreach: address }"></div> </script> <script type="text/html" id="child-template"> <p><span data-bind="text: region"></span>, <span data-bind="text: country"></span></p> </script> <script type="text/javascript"> function Address(region, country) { this.region = region; this.country = country; } function Person(name, address){ this.name = name; this.address = address; } function ViewModel() { this.people = ko.observableArray([ new Person("Bob", new Address("Kyoto", "Japan")), new Person("Mike", new Address("Tokyo", "Japan")), new Person("Katherine", new Address("New York", "America")) ]); } ko.applyBindings(new ViewModel()); </script>
as は foreach バインディング内で使う $parent のバインディングコンテキストのかっこいい書き方です。
公式サイトのサンプルはこんな感じです。
http://jsfiddle.net/tan_go238/70pd11kp/
<ul data-bind="template: { name: 'seasonTemplate', foreach: seasons, as: 'season' }"></ul> <script type="text/html" id="seasonTemplate"> <li> <strong data-bind="text: name"></strong> <ul data-bind="template: { name: 'monthTemplate', foreach: months, as: 'month' }"></ul> </li> </script> <script type="text/html" id="monthTemplate"> <li> <span data-bind="text: month"></span> is in <span data-bind="text: season.name"></span> </li> </script> <script> var viewModel = { seasons: ko.observableArray([ { name: 'Spring', months: [ 'March', 'April', 'May' ] }, { name: 'Summer', months: [ 'June', 'July', 'August' ] }, { name: 'Autumn', months: [ 'September', 'October', 'November' ] }, { name: 'Winter', months: [ 'December', 'January', 'February' ] } ]) }; ko.applyBindings(viewModel); </script>
ネストしたテンプレート内で親の変数が取得できます。
ではもうちょっと頑張って上のサンプルをもうひとつネストして子の子の子のテンプレートから同じように取得できるか試してみましょう。
http://jsfiddle.net/tan_go238/70pd11kp/1/
<ul data-bind="template: { name: 'seasonTemplate', foreach: seasons, as: 'season' }"></ul> <script type="text/html" id="seasonTemplate"> <li> <strong data-bind="text: name"></strong> <ul data-bind="template: { name: 'monthTemplate', foreach: months, as: 'month' }"></ul> </li> </script> <script type="text/html" id="monthTemplate"> <li> <span data-bind="template: { name: 'detailTemplate', data: month }"></span> </li> </script> <script type="text/html" id="detailTemplate"> <li> <span data-bind="text: name"></span> is in <span data-bind="text: season.name"></span> </li> </script> <script> var viewModel = { seasons: ko.observableArray([ { name: 'Spring', months: [ {name:'March'}, {name:'April'}, {name:'May'} ] }, { name: 'Summer', months: [ {name:'June'}, {name:'July'}, {name:'August'} ] }, { name: 'Autumn', months: [ {name:'September'}, {name:'October'}, {name:'November'} ] }, { name: 'Winter', months: [ {name:'December'}, {name:'January'}, {name:'February'} ] } ]) }; ko.applyBindings(viewModel); </script>
動作しました。asを使うといくつネストしようが親のプロパティの値を同じようにして取得できるみたいですね。
長くなってきましたので最後は一気にいきます。
afterRender, afterAdd, beforeRemove
これはテンプレート化したHTMLがレンダリングされたとき(afterRender)や、
foreachで追加されたとき(afterAdd)や削除された前(beforeRemove)に呼び出されるメソッドを指定する感じです。
http://jsfiddle.net/tan_go238/qncja7b8/1/
<h2>Participants</h2> Here are the participants:<br> <input type="checkbox" data-bind="checked: show1"> <br> <input type="checkbox" data-bind="checked: show2"> <br> <div data-bind="template: { name: 'person-template', data: buyer, if: show1 , afterRender: rendered }"></div> <div data-bind="template: { name: 'person-template', data: seller, if: show2 }"></div> <script type="text/html" id="person-template"> <h3 data-bind="text: name"></h3> <p>Credits: <span data-bind="text: credits"></span></p> </script> <script> function MyViewModel() { this.rendered = function(){ alert("rendered!") }; this.show1 = ko.observable(); this.show2 = ko.observable(); this.buyer = { name: 'Franklin', credits: 250 }; this.seller = { name: 'Mario', credits: 5800 }; } ko.applyBindings(new MyViewModel()); </script>
今日は templateバインディングを紹介しました。
templateバインディングはデフォルトで ko.nativeTemplateEngine を使用してレンダリングします。
もし jquery.tmpl を読み込んでいたら jquery.tmpl 用のテンプレートエンジン(ko.jqueryTmplTemplateEngine)を使用します。
※判定方法は ko.jqueryTmplTemplateEngine の jQueryTmplVersion あたりを参考にしてください。
明日は @isoden_ さんの「案件で使ってみてあーだったこーだった的な話です」です!
よろしくおねがいします!
カスタムバインディングの作り方
これはKnockoutJSアドベントカレンダー14日目の記事です。
KnockoutJS Advent Calendar 2014 - Qiita
KnockoutJSではすでに組み込まれているvalueバインディングやclickバインディングなど以外でも
自分でバインディングを作成することができます。
作り方は公式サイトにあります。
Knockout : Creating custom bindings
使い方は公式サイトのとおりです。
こういうふうに定義すると・・・・
ko.bindingHandlers.yourBindingName = { init: function(element, valueAccessor, allBindings, viewModel, bindingContext) { // This will be called when the binding is first applied to an element // Set up any initial state, event handlers, etc. here }, update: function(element, valueAccessor, allBindings, viewModel, bindingContext) { // This will be called once when the binding is first applied to an element, // and again whenever any observables/computeds that are accessed change // Update the DOM element based on the supplied values here. } };
こういうふうに使用できます。
<div data-bind="yourBindingName: someValue"> </div>
カスタムバインディングでは init と update のメソッドがあり、
初回時のロードで init 、 カスタムバインディングに適用した要素や依存しているobservables/computedsに更新があれば updateが実行されます。
よく使うメソッドの引数は以下のような感じです。
element
要素自体が渡されます。
valueAccessor
この要素にバインドされているViewModelのプロパティをメソッドで渡されます。
データを取得するには valueAccessor() のようにして取得します。
allBindings
この要素に対してdata-bindで渡されたすべてのデータが渡されます。
基本的な使い方は以上ですが、カスタムバインディングの実行前に実行されるメソッドも定義することができます。
ko.bindingHandlers.<name>.preprocess(value, name, addBindingCallback)
要素が使用されなくなったときに破棄したい場合は以下を使用すると良いです。
ko.utils.domNodeDisposal.addDisposeCallback(node, callback)
公式ドキュメントではそれぞれここに記載されています。
Knockout : Extending Knockout's binding syntax using preprocessing
Knockout : Custom disposal logic
最後に、だいぶ汚いコードですがサンプル書いてみました。
BootstrapのPopoverを使ってメモボタンをカスタムバインディングで書いてみました。
<a href="#" class="memo-btn" data-bind="memo:{ editable:true, target: 'memo1', content: memo1()}" data-placement="right" tabindex="-1" role="button">メモ</a> <input type="hidden" name="memo1" data-bind="value: memo1"/>
上記のように memo というバインディングハンドラを書いて中にオブジェクトで設定を渡しています。
1つ目の引数 "editable" は編集可能かどうか
2つめの引数 "targe" は保存時に更新するViewModelのプロパティ名
3つめの引数 "content" はPopoverに表示する値
のようにしています。
ちょっとした応用ですがこのように引数をオブジェクトで渡すことで設定値とかも渡せるので便利です。
また3.2では ここ で書いた Componentsを使うとさらに簡潔に書くことができます。
明日も自分です。書き溜めてた分がなくなったので頑張ります。
Yeomanを使ってKnockoutのプロジェクトを作成する
今回は Yeoman、 Gulp、Karma を使ってKnockoutJSのプロジェクトを作成しようと思います。
元ネタは作者のブログのここの動画です。
Steve Sanderson - Architecting large Single Page Applications with Knockout.js on Vimeo
この動画の内容を今回のアドベントカレンダーで何回かに分けて紹介したいと思います。
下準備
まず yeoman を以下のコマンドでインストールします。
npm install -g yo
つぎに KnockoutJSを使ったプロジェクトを生成するために generator-ko を以下のコマンドでインストールします。
npm install -g generator-ko
基本これでOKですが、ローカルで簡単に確認するためのWebサーバがさくっと用意できない人も多いと思いますので、簡易Webサーバを用意しておきましょう。
つぎのコマンドでインストールします。(このWebサーバの起動方法は後述)
npm install -g http-server
Scaffolding
ではプロジェクトの雛形を生成します。このように雛形を生成することをScaffoldingと呼びます。
最初にプロジェクトのディレクトリを作成し、以下の yo コマンドを実行します。
mkdir TVGuide cd TVGuide yo ko
すると以下のような画面になるので、気にせず全部エンターキーを押しましょう。
すると以下のような構造でファイルが作成されます。
プロジェクトの雛形が生成されたら、Webサーバを起動してブラウザで確認してみましょう。
以下のコマンドをプロジェクトの一番トップのディレクトリで実行します。
% pwd /path/to/TVGuide // プロジェクトの一番上のディレクトリ % http-server src Starting up http-server, serving src on: http://0.0.0.0:8080
ブラウザを開き以下のように表示されたら成功です。