パークのソフトウエア開発者ブログ|ICT技術(Java・Android・iPhone・C・Ruby)なら株式会社パークにお任せください -8ページ目

パークのソフトウエア開発者ブログ|ICT技術(Java・Android・iPhone・C・Ruby)なら株式会社パークにお任せください

開発の解決方法や新しい手法の情報を、パークのエンジニアが提供します。パークのエンジニアが必要な場合は、ぜひお気軽にお問い合わせ下さい。 株式会社パーク:http://www.pa-rk.co.jp/

サービスマネージャで任意のサービスを開始した時、
開始したサービスの状態欄がOSによって下記のように
異なりますが、同じ状態を表しています。

・Windows2012→「実行中」
・Windows2008→「開始」

サービスの状態は、コマンドプロンプトで所定の場所で
以下のコマンドを打つことでも確認できます。

C:\Windows\System32 > sc query <任意のサービス名>

※sc queryについては下記参照
https://technet.microsoft.com/ja-jp/en-vs/library/dd228922.aspx




ちかです
最近は TypeScript に浸かっています。
もう何年も Python に触れていなかったのですが
Python 3.5 には型ヒントやらコルーチンのための構文 (async/await) やら追加されて
少し気になっていました。

型ヒントについては Python と型ヒント (Type Hints) を読んで
構造的部分型の型チェック (TypeScript みたいな) ができるようになることに期待しつつ放置。

async/await は (少なくとも他の言語では神機能だし) 使ってみようかと。
特に非同期イテレーション専用の構文 async for がおもしろそう!
ということで
以前 Go 言語と D 言語でスリープソートを実装してみたのとの比較も込めて
スリープソートを実装してみました。

import asyncio
from contextlib import closing

class ArrivalOrderedIterator:
    ''' コルーチン (または Future) のリストを受け取り、結果の到着順に結果値を返す非同期イテレーターを表します。 '''
    def __init__(self, coroutines):
        self.completed = set()
        self.incomplete = [asyncio.ensure_future(co) for co in coroutines]
 
    async def __aiter__(self):
        return self
 
    async def __anext__(self):
        if self.completed:
            return self.completed.pop().result()
        if not self.incomplete:
            raise StopAsyncIteration()
        self.completed, self.incomplete = await asyncio.wait(self.incomplete, return_when=asyncio.FIRST_COMPLETED)
        return self.completed.pop().result()

class sleepsort:
    ''' 数値のリストをスリープソートする非同期シーケンスを表します。 '''
    def __init__(self, values):
        self.values = values
 
    async def __aiter__(self):
        return ArrivalOrderedIterator(asyncio.sleep(x, x) for x in self.values)

if __name__ == '__main__':
    async def main():
        async for value in sleepsort([5, 2, 19, 11, 7, 13, 3, 17]):
            print(value)
    with closing(asyncio.get_event_loop()) as loop:
        loop.run_until_complete(main())

故意に async for 文を使っています (31 行目)。
そのせいで必要以上に長いコードになっているかもしれません。

async for 文を使うことで非同期のイテレーションを読みやすく記述できます。
...利用側では。

async for value in async_iterable:
  do_something(value)

非同期イテレーターの実装側では
__anext__() メソッドで「次の値」を返すか StopAsyncIteration を発生させてループを終了させます。
基本的にはクラスを作成して現在の状態をフィールドに持たせることになると思います。
ジェネレーター関数みたいに記述できたらラクなのですが。。

また
Reactive Extensions (Rx) を彷彿とさせますが (私だけ?)
内包表記や map/filter などは使えないようで
必要なら自前で用意する必要がありそうです。。
((i * i async for value in sleepsort(values) if i < 10) みたいな構文はないみたいです。)
参考: python - how can I asynchronously map/filter an asynchronous iterable? - Stack Overflow

ということで Python 3.5 の非同期イテレーション構文の紹介でした。

追記: ちなみに async for を使わず素直にコールバック形式で書くなら:

import asyncio
from contextlib import closing

async def sleepsort(values, callback):
    ''' 数値のリストをスリープソートします。 '''
    incomplete = [asyncio.ensure_future(asyncio.sleep(x, x)) for x in values]
    while incomplete:
        completed, incomplete = await asyncio.wait(incomplete, return_when=asyncio.FIRST_COMPLETED)
        while completed:
            callback(completed.pop().result())

if __name__ == '__main__':
    with closing(asyncio.get_event_loop()) as loop:
        loop.run_until_complete(sleepsort([5, 2, 19, 11, 7, 13, 3, 17], print))

追々記: map/filter を実装してみました。

import asyncio
from functools import partial
from contextlib import closing

class AsyncIterator:
    ''' 「次の値を取得する関数」から非同期イテレーターを構築します。 '''
    def __init__(self, anext):
        self.anext = anext

    async def __aiter__(self):
        return self

    async def __anext__(self):
        return await self.anext()

def async_iterable(next_getter_creator):
    ''' 「「次の値を取得する関数」を取得する関数」から非同期シーケンスを構築します。 '''
    class AsyncIterable:
        def __init__(self, *args, **kwds):
            self.args = args
            self.kwds = kwds

        async def __aiter__(self):
            return AsyncIterator(await next_getter_creator(*self.args, **self.kwds))

    return AsyncIterable

@async_iterable
async def async_map(selector, source):
    ''' 非同期シーケンスを射影します。 '''
    iterator = await source.__aiter__()
    async def next():
        return selector(await iterator.__anext__())
    return next

@async_iterable
async def async_filter(predicate, source):
    ''' 非同期シーケンスを絞り込みます。 '''
    iterator = await source.__aiter__()
    async def next():
        while True:
            value = await iterator.__anext__()
            if predicate(value):
                return value
    return next

@async_iterable
async def sleepsort(values):
    ''' 数値のリストをスリープソートする非同期シーケンスを表します。 '''
    # tasks = (completed, incomplete)
    tasks = [set(), [asyncio.ensure_future(asyncio.sleep(x, x)) for x in values]]
    async def next():
        if tasks[0]:
            return tasks[0].pop().result()
        if not tasks[1]:
            raise StopAsyncIteration()
        tasks[0], tasks[1] = await asyncio.wait(tasks[1], return_when=asyncio.FIRST_COMPLETED)
        return tasks[0].pop().result()
    return next

def chain(source, *selectors):
    ''' f(g(h(x))) を chain(x, h, g, f) と記述するメソッドチェイン機構を提供します。 '''
    for selector in selectors:
        source = selector(source)
    return source

if __name__ == '__main__':
    async def main():
        async for value in chain(
            sleepsort([5, 2, 19, 11, 7, 13, 3, 17]),
            partial(async_map, lambda x: x * x),
            partial(async_filter, lambda x: 10 < x < 200)
        ):
            print(value)
    with closing(asyncio.get_event_loop()) as loop:
        loop.run_until_complete(main())

出力結果

25
49
121
169

(開始 5 秒後に 25, 7 秒後に 49, ... が出力されます)

はじめに

ちかです
毎度のことですが個人の見解です。
会社の見解ではありません。
むしろ他の社員からは反対意見が出そうな話です。

Web ページの構成について「控えめな JavaScript (Unobtrusive JavaScript)」という慣行があります。
太古の昔、 JavaScript は馬に乗って行列を作って HTML 上を走り回っていたそうです。
「控えめな JavaScript」は革命を起こし、横柄な JavaScript とは対極の立場を取りました。

『インラインスクリプトは絶対に書きません!』

でもこれはあまりに極端では?? というのが今回の記事です。

「控えめな JavaScript」信奉者の主張

『Rails で JavaScript を使用する』の例を見てみます。

最もシンプルなJavaScriptを例にとって考えてみましょう。以下のような書き方は'インラインJavaScript'と呼ばれています。

<a href="#" onclick="this.style.backgroundColor='#990000'">Paint it red</a>

このリンクをクリックすると、背景が赤くなります。しかし早くもここで問題が生じ始めます。クリックした時にJavaScriptでもっといろんなことをさせるとどうなるでしょうか。

<a href="#" onclick="this.style.backgroundColor='#009900';this.style.color='#FFFFFF';">Paint it green</a>

だいぶ乱雑になってきました。ではここで関数定義をclickハンドラの外に追い出し、CoffeeScriptで書き換えてみましょう。

paintIt = (element, backgroundColor, textColor) ->
  element.style.backgroundColor = backgroundColor
  if textColor?
    element.style.color = textColor

ページの内容は以下のとおりです。

<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a>

これでコードがだいぶ良くなりました。しかし、同じ効果を複数のリンクに与えるとどうなるでしょうか。

<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a>
<a href="#" onclick="paintIt(this, '#009900', '#FFFFFF')">Paint it green</a>
<a href="#" onclick="paintIt(this, '#000099', '#FFFFFF')">Paint it blue</a>

これではDRYとは言えません。今度はイベントを活用して改良してみましょう。最初にdata-*属性をリンクに追加しておきます。続いて、この属性を持つすべてのリンクで発生するクリックイベントにハンドラをバインドします。

paintIt = (element, backgroundColor, textColor) ->
  element.style.backgroundColor = backgroundColor
  if textColor?
    element.style.color = textColor
 
$ ->
  $("a[data-background-color]").click (e) ->
    e.preventDefault()
 
    backgroundColor = $(this).data("background-color")
    textColor = $(this).data("text-color")
    paintIt(this, backgroundColor, textColor)
<a href="#" data-background-color="#990000">Paint it red</a>
<a href="#" data-background-color="#009900" data-text-color="#FFFFFF">Paint it green</a>
<a href="#" data-background-color="#000099" data-text-color="#FFFFFF">Paint it blue</a>

私たちはこの手法を「控えめなJavaScript」と呼んでいます。この名称は、HTMLの中にJavaScriptを混入させないという意図に由来しています。JavaScriptを正しく分離することができたので、今後の変更が容易になりました。今後は、このdata-*属性をリンクタグに追加するだけでこの動作を簡単に追加できます。

直接 onclick とは書いていないだけ

> JavaScriptを正しく分離することができたので、今後の変更が容易になりました。今後は、このdata-*属性をリンクタグに追加するだけでこの動作を簡単に追加できます。

とおっしゃっておられますが
分離前でも onclick="paintIt(this, '#990000')" のような記述を追加するだけでこの動作を簡単に追加できていました。
なにをもって分離後の方が「変更が容易」と言えるのでしょう?
「JavaScriptを正しく分離することができたので」では全く理由になっていません。
(「これではDRYとは言えません。」という部分も何が DRY でないのか自明でなく説明もありません。。)

コンテンツ (HTML) と振る舞い (JavaScript) と見た目 (CSS) とを別ファイルにする目的は
関心を分離して互いを抽象化することにあると思うのですが
マークアップには振る舞いを表現するためのクラスや data-* 属性が必要で
抽象化は実現できておらず
何が変わったかと言えば単に「直接 onclick とは書いていない」だけです。

実際には関心は分離できていないのです。

grep しないと理解できないコード

それどころか
無視できない大きなデメリットがあります。
関係のある記述が散らばることです。

端的にビフォーアフターで比較しましょう。
レンダリング結果が予測しやすいのはどちらでしょうか?

<a href="#" onclick="paintIt(this, '#000099', '#FFFFFF')">Paint it blue</a>
<a href="#" data-background-color="#000099" data-text-color="#FFFFFF">Paint it blue</a>

コードを書いているときはまだいいのですが
後になって HTML を読んでみると
data-background-color 属性が何を表しているのか
見た目にどう影響するのか
振る舞いにどう影響するのか
grep しなければ分かりません。

やがて HTML は得体の知れないクラス名や属性で埋め尽くされます。
そして JavaScript はイベントリスナーの登録処理で埋め尽くされます。
ビジネスロジックに集中できない!

『間違ったコードは間違って見えるようにする』 の言葉を少し借りると、

関係するものを隣り合わせにしておくことでコードを改善できる例はたくさんある。コーディング規則の多くは以下のような規則を含んでいる:

  • 関数を短くする。
  • 変数は使う場所のできるだけ近くで宣言する。
  • 自分のプログラミング言語を作ろうとしてマクロを使わない。
  • gotoを使わない。
  • 閉じる括弧は、対応する開く括弧から1画面以上離さない。

これらのルールに共通しているのは、1行のコードが実際にすることに関連した情報を可能な限り物理的に近づけるということだ。そうすることによって、あなたの目玉が何が起きているのか理解できる可能性が高くなる。

(だからといってハンガリアン記法はオススメしませんが。。)

もっと地味な問題: コードの恣意性

もっと地味ぃ~な問題もあります。
コードの恣意性が強くなることです。

例えば:
div 要素 A ではクリック時に処理 1 の結果をもとに処理 2 を行いたいとします。
div 要素 B ではクリック時に処理 1 の結果をもとに処理 3 を行いたいとします。

インラインスクリプトで実現するならこうです。

<div onclick="procedure2(procedure1('arg1'));"> ... </div>
<div onclick="procedure3(procedure1('arg2'));"> ... </div>

インラインスクリプトを使わないコードを想像してみてください。
しっくり来るでしょうか?
その「しっくり来たコード」は誰が考えても似たようになるでしょうか?
しっくり来るまでにかかる時間はインラインスクリプトで実現するのに比べて長くならないでしょうか?
(この例ではインラインスクリプトが見えているのでアレですが)

これらは「やり方がいろいろある」ことの弊害です。
スクリプトを分離しようと考えると、コードの恣意性が高まります。
(恣意性が高くなる原因についてはパッと説明できませんが。。)

やり方がいろいろあると
思いつく限りの方法の良し悪しを比較する必要があり
「しっくり来る」のに時間がかかるのです。
間違えようのないやり方がひとつだけあるのがいいと考えるのは私だけではないはずです。

React

コンテンツとその振る舞いは切っても切れません。
コンテンツとその振る舞いは同じ場所に記述した方がメンテナンスしやすくなります。
などと私ごときが言っても伝わらないかもしれませんが...
React の JSX コードがどうして次のようになるのか (次のように書けると何が嬉しいのか)
考えてみてはいかがでしょうか。

  • DOM Event Listeners in a Component | React
    var Box = React.createClass({
      getInitialState: function() {
        return {windowWidth: window.innerWidth};
      },
    
      handleResize: function(e) {
        this.setState({windowWidth: window.innerWidth});
      },
    
      componentDidMount: function() {
        window.addEventListener('resize', this.handleResize);
      },
    
      componentWillUnmount: function() {
        window.removeEventListener('resize', this.handleResize);
      },
    
      render: function() {
        return <div>Current window width: {this.state.windowWidth}</div>;
      }
    });
    
    ReactDOM.render(<Box />, mountNode);
    

だいたい以上です

インラインスクリプト禁止の弊害について書いてきました。
やたらめったらインラインスクリプトを使えと言うつもりはありませんし
プロジェクトのポリシーとしてインラインスクリプト禁止と言われれば私もインラインスクリプトは使いません。
ただ
思考を停止してインラインスクリプトを悪とする風潮に対しては冷静になりたいと思った次第です。

p.s. インラインスタイルについて

インラインスタイルが悪かどうかについては検討できていません。
いろいろ思うところはありますが
ノーコメントにしておきます。

どうも、ちかです
最近めっちゃ忙しい。。

Kibana ってこんなの

Elasticsearch のデータを可視化するツールです。

こういうブログ記事では
まず導入として
Kibana ってこんなの」っていう話をするものですが
すでに知ってる人向けの記事ということで。

Kibana プラグイン

Kibana 4.2 からプラグインで機能を追加できるようになりました。

サーバー側は Node.js のフレームワーク hapi ベースで
クライアント側は AngularJS ベースで
とりあえずなんでもできます。

特に Elasticsearch へのアクセスは
サーバーから手軽にできるようになっています。
サーバー側でプロキシを作ればクライアントからも簡単にアクセスできます。

# Elasticsearch への GET メソッドのクチはデフォルトで開いてます。
# elasticsearch.js の API を
# サーバーでは server.plugins.elasticsearch.client として使えます。
# クライアントでは es という名前で依存性注入できます。

Kibana プラグインの作り方

Qiita に
Kibanaプラットフォームでアプリケーションをつくってみよう
という濃いぃ記事があるのでご参考に。

ミニマム構成では index.js と package.json があれば OK。

認証プラグイン


Kibana を使ってみて気になったのが
認証機能がないこと。

Shield という Elasticsearch プラグインがあるらしいのですが
有償らしいので (セキュリティ向けプラグインShieldのリリース(日本語訳))
とりあえずあきらめて

Authentication という Issue ページも立っているのですが
4.2 ⇒ 4.3 ⇒ 4.4 と先延ばし先延ばしにされているので
とりあえずあきらめて

作ってみました。

  • index.js
const Boom = require('boom');

var users = {
  chica: {
    password: 'secret'
  }
};

module.exports = kibana => new kibana.Plugin({
  init(server, options) {
    server.auth.scheme('basic', basic);
    server.auth.strategy('default', 'basic');
    server.auth.default('default');
  }
});

function basic(server, options) {
  return {
    authenticate(request, reply) {
      const authorization = request.raw.req.headers.authorization;
      if (!authorization) {
        return reply(Boom.unauthorized('Authentication required', 'Basic'));
      }

      const parts = authorization.split(/\s+/);
      if (parts.length !== 2 || parts[0].toLowerCase() !== 'basic') {
        return reply(Boom.unauthorized('Authentication failed.', 'Basic'));
      }

      const credentials = new Buffer(parts[1], 'base64').toString().split(':', 2);
      const user = users[credentials[0]];
      if (!user || user.password !== credentials[1]) {
        return reply(Boom.unauthorized('Authentication failed.', 'Basic'));
      }

      return reply.continue({ credentials: { user: user } });
    }
  };
}
  • package.json
{
  "name": "basic-authentication",
  "version": "0.0.1"
}

index.js と package.json を tar.gz に固めて

kibana/bin/kibana plugin -i basic-authentication -u file://path/to/plugin.tar.gz

こんなので
一応ちゃんと認証が求められるようになります。

まぁでも
これを洗練するより
4.4 が出るのを待つかな。(本末転倒)

今回はこんなところで。



こんばんは。ゆんぼうです。

今回は、Backbone.jsを使用して、チェックボックスを表示するテーブルを作成します。

今回使用する環境は下記の通りです。

Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/

JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/



■チェックボックスのテーブル

【デモはこちら】
【ソースファイルはこちら】

チェックボックス付きのテーブルと登録・削除ボタンを作成します。
名前のテキストボックスに入力して、登録ボタンを押下すると、
テーブルにチェックボックスと名前が追加されます。
チェックボックスを選択して、削除ボタンを押下すると、そのテーブルデータが削除されます。
また、全てを選択するチェックボックスを押下すると、各チェックボックスが併せて変更されます。

index.html のhead に下記の内容を記述します。


<script type="text/template" id="template-user-table">
<input id="id_button_delete" type="button" value="<%= label.deleteButton %>">
<hr>
<table class="cls_table">
<thead>
<tr>
<th><input class="cls_all_checkbox" type="checkbox"></th>
<th><%= header.name %></th>
</tr>
</thead>
<tbody id="id_tbody">
</tbody>
</table>
</script>
<script type="text/template" id="template-user-table-item">
<th>
<input class="cls_checkbox" type="checkbox">
</th>
<td><%= data.name %></td>
</script>


テーブル全体のテンプレート template-user-table を作成します。
全て選択用のチェックボックスのスタイルクラス名は cls_all_checkbox と定義します。

テーブルデータ1件分のテンプレート template-user-table-item を作成します。
チェックボックスのスタイルクラス名は cls_checkbox と定義します。

main.js に下記の内容を記述します。


var UserCollection = Backbone.Collection.extend({
model : UserModel,
removeSelectedModel : function() {
var selectedCollection = this.models.filter(function(model) { // (17)
return model.isSelected;
});
this.remove(selectedCollection); // (18)
},
isSelectedAll : false,
selectModelAll : function(isSelected) {
this.isSelectedAll = isSelected;
_.each(this.models, function(model) { // (10)
model.isSelected = isSelected;
});
this.trigger('selectAll'); // (11)
},
selectModel : function() {
this.isSelectedAll = this.models.every(function(model) { // (3)
return model.isSelected
});
this.trigger('select'); // (4)
}
});

var UserTableView = Backbone.View.extend({
name : 'UserTableView',
events : {
'click .cls_all_checkbox' : 'onClickAllCheckbox', // (8)
'click #id_button_delete' : 'onClickDeleteButton' // (15)
},
initialize : function(args) {
this.collection = args.collection;
this.listenTo(this.collection, 'add', this.onAdd);
this.listenTo(this.collection, 'remove', this.onRemove); // (19)
this.listenTo(this.collection, 'select', this.onSelect); // (5)
this.listenTo(this.collection, 'selectAll', this.onSelectAll); // (12)
this.compiledTemplate = _.template($('#template-user-table').text());
this.currentHeader = null;
},
render : function() {
var tempData = {
'header' : {
'name' : '名前'
},
'label' : {
'deleteButton' : '削除'
}
};
this.$el.append(this.compiledTemplate(tempData));
return this;
},
renderItem : function() {
this.$el.find('#id_tbody').html('');
_.each(this.collection.models, function(model) {
this.$el.find('#id_tbody').append((new UserTableItemView({
'model' : model,
'collection' : this.collection
})).render().el);
}, this);
},
renderSelectAll : function() {
this.$el.find('.cls_all_checkbox').prop('checked', this.collection.isSelectedAll) // (7)
},
onAdd : function() {
console.log(this.name + '#onAdd');
this.renderItem();
},
onRemove : function() {
console.log(this.name + '#onRemove');
this.renderItem(); // (20)
},
onSelect : function() {
console.log(this.name + '#onSelect');
this.renderSelectAll(); // (6)
},
onSelectAll : function() {
console.log(this.name + '#onSelectAll');
this.renderItem(); // (13)
},
onClickAllCheckbox : function(event) {
console.log(this.name + '#onClickAllCheckbox');
this.collection.selectModelAll(event.target.checked); // (9)
},
onClickDeleteButton : function(event) {
console.log(this.name + '#onClickDeleteButton');
this.collection.removeSelectedModel(); // (16)
}
});

var UserTableItemView = Backbone.View.extend({
name : 'UserTableItemView',
tagName : 'tr',
events : {
'click .cls_checkbox' : 'onClickCheckbox' // (1)
},
initialize : function(args) {
this.model = args.model;
this.collection = args.collection;
this.compiledTemplate = _.template($('#template-user-table-item').text());
},
render : function() {
var templateData = {
'data' : this.model.toJSON()
};
this.$el.append(this.compiledTemplate(templateData));
if (this.model.isSelected) {
this.$el.find('.cls_checkbox').prop('checked', true) // (14)
}
return this;
},
onClickCheckbox : function(event) {
console.log(this.name + '#onClickCheckbox:' + event.target.checked);
this.model.isSelected = event.target.checked; // (2)
this.collection.selectModel();
}
});


各チェックボックスを押下したとき

(1) 各テーブルデータのチェックボックスのクリックイベントを監視します。
(2) チェックボックスを押下したとき、モデルの選択フラグ isSelected にチェック状態を設定します。また、コレクションの selectModel 関数を呼びます。
(3) コレクションの every 関数を使用して、全てのモデルの isSelected が true かどうかを真偽値を isSelectedAll に設定します。
(4) select イベントを発火します。
(5) select イベントを監視します。
(6) 全て選択用のチェックボックスを描画します。
(7) 全て選択用のチェックボックスのチェック状態は、コレクションの isSelectedAll プロパティの値で設定します。

全て選択用のチェックボックスを押下したとき

(8) 全て選択用のチェックボックスのクリックイベントを監視します。
(9) 全て選択用のチェックボックスを押下したとき、チェック状態を引数にして、コレクションの selectModelAll 関数を呼び出します。
(10) 全てのモデルの isSelected を同じ値に設定します。
(11) selectAll イベントを発火します。
(12) selectAll イベントを監視します。
(13) 各テーブルデータを描画します。
(14) モデルの isSelected が true のとき、チェックボックスのチェックを設定します。

削除ボタンを押下したとき

(15) 削除ボタンのクリックイベントを監視します。
(16) 削除ボタンを押下したとき、コレクションの removeSelectedModel 関数を呼びます。
(17) filter 関数を使用して、isSelected が true のモデル配列を取得します。
(18) コレクションの remove 関数を呼び出して、モデルを削除します。モデル配列が複数あるときは、複数のモデルを削除します。
(19) remove イベントを監視します。
(20) 各テーブルデータを描画します。



■チェックボックスのテーブル (ソート・複数のヘッダー)

【デモはこちら】
【ソースファイルはこちら】

先ほどのチェックボックスのテーブルに、複数のヘッダーとソート機能を追加します。
これらの追加に対する処理はありません。そのまま追加します。
リンク先のソースファイルをご覧ください。

以上です。



□過去の記事

Backbone.js入門その11「ソートテーブルを作る」
Backbone.js入門その10「テーブルと登録ボタンを作る」
Backbone.js入門その9「リストと詳細ボタンを作る」
Backbone.js入門その8「リストと削除ボタンを作る」
Backbone.js入門その7「リストと登録ボタンを作る」
Backbone.js入門その6「ModelとCollection」
Backbone.js入門その5「静的HTMLでBackbone.Viewを作る」
Backbone.js入門その4「SuperViewとSubViewのアクセスを作成する」
Backbone.js入門その3「Backbone.ViewでSubViewを作る」
Backbone.js入門その2「Backbone.Viewで複数の要素を作る」
Backbone.js入門その1「Backbone.Viewで1つの要素を作る」