2011年8月26日

JavaScriptMVC を使ってみる

JavaScriptMVC を使ってプロジェクトを作成します。 この記事の続きです。

ツールの準備

github から ZIP パッケージをダウンロードして適当なディレクトリに展開します。 この記事を書いている時点での最新版は javascriptmvc-3.1.0.zip です。

中身を見ておくと次のようになります。

$ ls
MIT-LICENSE.txt  documentjs/      jquery/          js.bat*
changelog.txt    funcunit/        js*              steal/

$ ls steal/ jquery/ funcunit/ documentjs/
documentjs/:
README         distance.js    doc.bat        jmvcdoc/       scripts/       showdown.js    test/          update
build.js       doc*           documentjs.js  json.js        searchdata.js  tags/          types/

funcunit/:
README              drivers/            generate_docs.html  qunit/              summary.ejs
autosuggest/        envjs*              index.html          qunit.html          syn/
build.js            envjs.bat*          java/               resources/          template.html
dependencies.json   funcunit.html       loader.js           scripts/            test/
docs.html           funcunit.js         pages/              settings.js         update

jquery/:
README       buildAll.js  controller/  download/    generate/    lang/        qunit.html   tie/         view/
build.js     class/       dom/         event/       jquery.js    model/       test/        update

steal/:
README               coffee/              getjs                parse/               steal.production.js
build/               dev/                 js*                  patchfile            test/
buildjs              end.js               js.bat               pluginifyjs          update
clean/               generate/            less/                rhino/
cleanjs              get/                 make.js              steal.js

単純な JavaScript だけでなく、 QUnit, CoffeeScript, LESS なども同梱されていることが分かります。 また、 steal の中には Rhino もありますね。 ちょっと JavaScript を動かしてみるには良い環境だと思います。

アプリケーションの作成

js コマンドが Rhino へのラッパーになっており、 steal を使ってアプリケーションを管理します。

"cookbook" アプリケーションを作成してみます。

$ ./js steal/generate/app cookbook
      cookbook/cookbook.css
      cookbook/cookbook.html
      cookbook/cookbook.js
      cookbook/docs
      cookbook/resources
      cookbook/resources/example.coffee
      cookbook/resources/example.js
      cookbook/resources/example.less
      cookbook/scripts
      cookbook/scripts/build.html
      cookbook/scripts/build.js
      cookbook/scripts/clean.js
      cookbook/test

cookbook/cookbook.html を開いてエラーが出ないことを確認します。 StealJS がパス解決に失敗している部分があると後で大変なので、 FireBug や開発者コンソールなどで確認しておきましょう。

ファイルシステム上のファイルを直接開くと Cross origin リクエストのエラーになるブラウザもあります。 HTTP サーバを起動しておくと便利です。 js コマンドを実行したディレクトリで python -m SimpleHTTPServer を実行すると、 8000番ポートからアクセスできます。 Mac OS X の場合は Web 共有で Apache が起動しますので、 ~/Sites 以下に配置すれば HTTP 経由でアクセスできます。

次に、データモデルを定義します。 scaffold を使うことで、MVC で言うモデルとコントローラーを生成してくれます。

$ mkdir cookbook/models
$ ./js jquery/generate/scaffold Cookbook.Models.Recipe
      cookbook/controllers
      cookbook/controllers/recipe_controller.js
      cookbook/fixtures
      cookbook/fixtures/recipes.json.get
      cookbook/models
      cookbook/models/recipe.js
      cookbook/test
      cookbook/test/funcunit
      cookbook/test/funcunit/recipe_controller_test.js
      cookbook/test/qunit
      cookbook/test/qunit/recipe_test.js
      cookbook/views
      cookbook/views/recipe
      cookbook/views/recipe/edit.ejs
      cookbook/views/recipe/init.ejs
      cookbook/views/recipe/list.ejs
      cookbook/views/recipe/show.ejs

ここでサクッとモデルとコントローラーを使えると良いのですが、 JavaScriptMVC 2 系まであった .models().controllers() インターフェイスは廃止されました。

そこで、 cookbook.js を以下のように編集します。 コードジェネレーターで生成されたファイルは LESSCoffeeScript を読み込むようになっていますが、 この部分は一旦コメントアウトか削除しておきます。

steal
    .plugins('jquery/model', 'jquery/view/ejs')
    .then('models/recipe')
    .then(function() {
        $('#content').append($.View('//cookbook/views/recipe/init.ejs', {recipes: []}));
    })
    ;

まず、モデルとビューのプラグインを読み込みます。 ビューには EJS (EmbeddedJS), Micro, jQuery.Tmpl, JAML がありますが、 コードジェネレーターのデフォルトが EJS なので、これをそのまま使います。

次に、 cookbook/models/recipe.js で定義されるモデルクラス (Cookbook.Models.Recipe) を読み込みます。 .then() で指定する場合には .js の拡張子を省略します。 モデルクラスをここで読み込むのは、後から読み込むテンプレートファイルで参照しているためです。

最後の .then() にはコールバック関数を指定します。 最初に生成した cookbook.html には ID が "content" の <div> タグがありますので、 そこに HTML 要素を追加します。 $.View の最初の引数にテンプレートファイルへのパスを指定し、 二つ目の引数でパラメータを渡します。 パス表記はスラッシュふたつで始めています。 Web サーバ上での配置を考えるのが面倒な場合にはこのように書いておきます。

HTML ファイル (cookbook.html) をブラウザで開いてみると、 レシピの一覧と新しいレシピを登録するフォームのひな形が表示されています。

これでアプリケーションを実装する準備ができました。

データの表示

一覧表示

サンプルデータを表示させます。 cookbook/fixtures/recipes.json.get の中身を cookbook.js にコピーし、 テンプレートに渡します。 先ほどのコールバック関数を次のように変更します。

.then(function() {
    var recipes = [
        {"name": "Take Out Trash", "description": "To the curb!", "id": 5}
    ];
    $('#content').append($.View('//cookbook/views/recipe/init.ejs', {recipes: recipes}));
})

続いてテンプレートを修正します。 init.ejslist.ejs を呼び出し、その中で要素をループさせて show.ejs を呼び出します。 自動生成された show.ejs ではエラーが発生しますので、以下のように書き換えます。

<%for(var attribute in this){%>
    <%if(attribute == 'id') continue;%>
    <td class='<%= attribute%>' style="border:solid 1px gray; padding: 5px;">
        <%=this[attribute]%>
    </td>
<%}%>
<td>
    <a href='javascript://' class='edit'>edit</a>
    <a href='javascript://' class='destroy'>destroy</a>
</td>

cookbook.html をリロードすると次のようになります。 サンプルデータ (recipes 変数) を増やすと、一覧表示のデータに反映されます。

フォーム作成

モデルに属性を追加します。 Cookbook.Models.Recipe にはスタティックメソッドとプロトタイプメソッドが定義されています。 /* @Static */ のどこかに attributes を追加します。 辞書のキーはデータのキーと合わます。バリューは適当に当てています。

attributes: {
    'id': 'integer',
    'name': 'string',
    'description': 'text'
}

ブラウザで確認してみると、"New recipe" の部分にふたつのテキストボックスが追加され、 "recipes" の一覧表にタイトルが追加されていますね。

スタイルシートの分離

先ほどはテンプレートファイルの中に style 属性を記述しました。 これを外部ファイル化します。 方法はふたつあり、通常の CSS で管理する方法と、LESS を使う方法です。

CSS を読み込む

CSS は steal.css() メソッドで読み込めます。 cookbook.jssteal のメソッドチェーンに次の1行を追加します。

.css('cookbook')

<td> 要素に埋め込んだ style 属性を cookbook.css に移動してから cookbook.html を再度読み込みます。 デフォルトのデザインもかかってきますので、フォントなどが変わります。

LESS を使う

LESS を使うためには、プラグインを読み込んでから .less ファイルを指定します。 先ほどの .css() を次の4行で置き換えます。 もちろん、CSS も LESS も両方使う場合には、置き換えではなく付け足しになります。

.plugins('steal/less')
.then(function() {
    steal.less('resources/example');
})

cookbook/resources/example.less<td> の枠線を定義すると反映されます。

LESS を使うと変数を再利用できますので、素朴に CSS を記述していくよりは効率的にスタイルを管理できそうです。

終わりに

JavaScriptMVC を使ってアプリケーションのひな形と、簡単なデータを表示しました。 表示方法は複数のテンプレートシステムから選択でき、デザインには CSS だけでなく LESS も使えます。

新しいテンプレートや LESS、それから CoffeeScript は、 記法に慣れてしまえばサクサクと実装できるでしょうが、 不慣れな場合には覚えることが多くなってしまうことも事実です。 とはいえ、愚直に JavaScript や CSS を書いてブラウザごとの違いに泣かされるよりは、 最近流行り始めているかもしれない手法を取り入れた方が手っ取り早いと思います。

次は、QUnit と funcunit を使ってテストを動かしてみます。

コメントを投稿