[Rails4] Vue.js の導入手順 ＆ DBデータを表示する方法


Rails に、MVVM の JavaScript ライブラリである **Vue.js** を導入し、データベースの中身を表示するまでやってみます。

> Vue.js は レガシーブラウザ（例：IE8系）は非対応なので注意ください。

### 前提

- 手元の OS：`Mac OS X 10.9.5 (Mavericks)`
- Ruby：`2.1.2`
- Rails：`4.1.1`

# 環境構築

## ① Railsのプロジェクト作成

```shell:
$ bundle exec rails new hoge_app --skip-bundle -T
```

> - テストは後々、Rspec を使いたいので `-T` を指定。
> - データベースはデフォルトの sqlite3 を今回は採用。

## ② Gemfileの編集

Vue.js だけを使うなら `gem 'vuejs-rails'` のみでいいのですが、最低限のデザインの為の Twitter Bootstrap、テンプレート用の Haml、あとはデバックで必要そうな gem も導入しておきます。

```ruby:rails_angular_app/Gemfile
#..

gem 'vuejs-rails'
gem 'bootstrap-sass'
gem 'haml-rails'
gem 'erb2haml', group: :development

gem 'better_errors', group: :development
gem 'binding_of_caller', group: :development
gem 'pry-rails', group: :development

#..
```

## ③ Bundler で gem をインストール

```shell:
$ cd hoge_app
$ bundle install --path vendor/bundle
```

- インストールが終わったら一旦、ここで動作確認。ブラウザで `http://127.0.0.1:3000` にアクセスし、お馴染みの画面が表示されればOK。

	```shell:
	$ cd hoge_app
	$ ./bin/rails s
	```

## ④ Spring の有効化

とくに Vue.js とは関係ありませんが、やっておきます。

```shell:
$ cd hoge_app
$ bundle exec spring binstub --all
```

## ⑤ Haml 関連の設定

既存の `.erb` ファイルを `.haml` ファイルへ変換。

```shell:
$ cd hoge_app
$ ./bin/rake haml:replace_erbs
```

## ⑥ Vue.js 関連の設定

- `app/assets/javascripts/application.js` に追記。

	```javascript:app/assets/javascripts/application.js
	〜

	//= require vue

	〜
	```

## ⑦ Twitter Bootstrap 関連の設定

- `app/assets/stylesheets/application.css.scss` を新規に作成して編集。

	```shell:
	$ touch app/assets/stylesheets/application.css.scss
	```

	```app/assets/stylesheets/application.css.scss
	@import "bootstrap-sprockets";
	@import "bootstrap";
	```

	> 行の順番を入れ替えると動かないかもなので注意。

- 既存の `app/assets/stylesheets/application.css` は削除。

	```shell
	$ rm app/assets/stylesheets/application.css
	```

- `app/assets/javascripts/application.js` に追記。

	```app/assets/javascripts/application.js
	〜

	//= require bootstrap-sprockets

	〜
	```

# 作った環境の動作確認

## ① とりあえず適当に Scaffold

```shell:
$ ./bin/rails g scaffold person name:string age:integer memo:text
$ ./bin/rake db:migrate
```

## ② Bootstrap の動作確認

- 共通ページレイアウトを修正。

	```haml:app/views/layouts/application.html.haml
	!!!
	%html
	  %head
	    %title HogeApp
	    = stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true
	    = javascript_include_tag 'application', 'data-turbolinks-track' => true
	    = csrf_meta_tags
	  %body
	    %nav.navbar.navbar-default{role: "navigation"}
	      .container-fluid
	        .navbar-header
	          %a.navbar-brand{href: "#"}
	            home
	    .container
	      = yield
	```

- 表示を確認。

	```shell:
	$ ./bin/rails s
	```

	ブラウザで `http://127.0.0.1:3000/people` にアクセス。

	![screen2.png](https://qiita-image-store.s3.amazonaws.com/0/35733/d0f20e06-c010-4316-1257-cd1f9325f68f.png "screen2.png")

## ③ Vue.js の動作確認

### View

```haml:app/views/people/index.html.haml
〜

#demo
  %p
    {{lastName}} {{firstName}}
  %p
    %input{"v-model" => "lastName"}/
    %input{"v-model" => "firstName"}/

  %br
  %br

  %p
    {{message}}
  %p
    %button.btn.btn-primary{"v-on" => "click: execute"} 実行
```

> Haml 記法です。

### ViewModel

```coffeescript:app/assets/javascripts/people.js.coffee
$ ->
  demo = new Vue(
    el: "#demo"
    data:
      firstName: "太郎"
      lastName: "山田"
    methods:
      execute: ->
        @message = "実行しました"
        return
  )

  return
```

次のようにデータバインドが動いていればOK。

![screen1.png](https://qiita-image-store.s3.amazonaws.com/0/35733/de5f79c9-dfbb-3444-f56e-c0ce88202187.png "screen1.png")

# データベースの中身を Vue.js で描写してみる

Scaffold で作成した `Person` モデルのデータを、一覧ページ(`:index`)において Vue.js で描写してみます。

## ① Rails の View

```haml:app/views/people/index.html.haml
%h1 Listing people

#index

  :coffee
    rails = window.rails = window.rails ? {}
    rails.objPeople= #{raw @people.to_json}

  %table.table.table-striped
    %thead
      %tr
        %th 名前
        %th 年齢
        %th メモ
    %tbody{'v-repeat' => 'items'}
      %tr
        %td
          {{name}}
        %td
          {{age}}
        %td
          {{memo}}
  %P
    %a{:href => '/people/new'} New Person
```

CoffeeScript 部分では、`rails = window.rails = window.rails ? {}` のようにして、この View 以外のファイルから参照できるよう、変数 `rails` をグローバルにエクスポートしています。

`rails.objPeople= #{raw @people.to_json}` により、Viewの出力時に `Person` モデルのデータの中身が JavaScript のオブジェクトへ代入されます。

.. Vue.js はすっきり書けていいですね^^

## ② Vue.js の ViewModel

```coffeescript:app/assets/javascripts/people.js.coffee
$(document).on('ready page:load', ->

  if $("#index")[0]
    new Vue(
      el: "#index"
      data:
        items: rails.objPeople
    )

  return
)
```

`new Vue()` により ViewModel を定義します。`el` プロパティによりバインド対象とする div エリアを指定、`data` プロパティでは、先程エクスポートした `Person` モデルのデータを 変数 `items` に紐づけます。

### 注意１

Rails の turbolinks 下で、この ViewModel を「初期読み込み時」「ページ遷移時」で動かすために次のようにしています。

```coffeescript:
$(document).on('ready page:load', ->
  〜
)
```

上記の `page:load` イベントは、純粋な jQuery のイベントではなく、turbolinks 用に拡張されたイベントです。

> 詳しくはこちら ⇒ https://github.com/rails/turbolinks

### 注意２

`if $("#index")[0]` のようにして、「id='index'のdivエリアが存在する」場合に限って ViewModel を作成するようにしています。これは Rails が、どの Controller であるかに関わらず `app/assets/javascripts/` ディレクトリ下のファイル全てを読み込んでしまうため、ほかの View 用の ViewModel を読み込んだ場合に div エリアが存在しない旨の JavaScript エラーが出てしまうからです。

## ③ 動作確認

Vue.js で描写できました^^

![スクリーンショット 2014-10-11 23.16.14.png](https://qiita-image-store.s3.amazonaws.com/0/35733/b3ca645c-e649-e268-7531-b4d72f9f62ea.png "スクリーンショット 2014-10-11 23.16.14.png")

今回のソースは GitHub に置いておきます。手順通りやっても動かないぞ、という場合はこちらをご覧ください。
⇒ https://github.com/hkusu/Vue.js_Rails_seed

# 参考URL

といっても前に自分で書いたものですが..

- [シンプルな MVVM ライブラリ『Vue.js』の環境構築と使ってみた感想](http://qiita.com/hkusu/items/8af744662999de618bb8)
- [リッチなUIを実現する為に。Rails⇔JavaScriptフレームワーク間でデータを受け渡す方法](http://qiita.com/hkusu/items/462d8bbe7c4dfe1877c8)
- [Rails4 で Knockout ＆ Bootstrap ＆ Coffee ＆ SASS ＆ Haml な環境を構築する](http://qiita.com/hkusu/items/0c68e294521b7e5caf55)