FrontEnd

Vue.jsとRailsでTODOアプリのチュートリアルみたいなものを作ってみた【2018.03.24】

投稿日:2018年4月9日 更新日:

はじめに

以前、Qiitaに書いた記事ですが、いくつか現行バージョンではうまくいかないという指摘があったため、やり直してみました。

各バージョンは、2018年3月24日時点の安定版で試しています。

作るもの

簡単なTODOアプリです。

TODOの管理はRailsのAPIで実施します。

前提条件、及び環境

Ruby、Rails、Node.jsの環境をご用意ください。
下記の記事などが分かりやすいです。

また、Webpack、およびVue.jsはyarnでインストールされるため、yarnもインストールしておいてください。
yarnを使ってみた

本記事を書くにあたり、下記の環境で実施しました。

  • Ruby 2.5.0
  • Ruby on Rails 5.1.5
  • Node.js 9.9.0
  • yarn 1.5.1
  • rails/webpacker 3.3.1
  • Vue.js 2.5.16

Webpack について

複数のJSファイルをひとまとめにするモジュールバンドラーのことです。
複数ファイルに分割して管理しつつ、サーバーへのリクエスト数を削減することができるようです。
最新版で学ぶwebpack 3入門 – JavaScript開発で人気のバンドルツール

準備

まずはもろもろインストールをしていきます。
なお、こちらの記事が動画つきで非常に分かりやすいので、もし初めて実施される方はご覧になった方が良いかと思います。
【動画付き】Rails 5.1で作るVue.jsアプリケーション ~Herokuデプロイからシステムテストまで~

Rails + Vue.jsのプロジェクトを作成する

はじめから--webpackオプションを使用してプロジェクトを作成します。

rails sでRailsのウェルカムページが表示されれば大丈夫です。

Vue.jsの表示確認

基本的に、Railsで用意するビューファイルは1つのみで、そこを差し替えていきます。
まずは、以下のファイルを作成、編集します。

  • app/controllers/home_controller.rb
  • config/routes.rb
  • app/views/home/index.html.erb

javascript_pack_tagを使用することで、app/javascript/packs以下にあるJSファイルを探してくれます。
インストール時にhello_vue.jsというファイルが生成されているので、これをindexにて読み込ませます。
これでrails sして、「Hello Vue!」と表示されれば大丈夫です。

devサーバーを設定する

Vue.js(というよりWebpack)関連のファイルは変更したらコンパイルする必要があります。
コンパイルには、bin/webpackというコマンドを使用する必要があります。

ただ、毎回コンパイルするのは面倒なので、変更を検出して自動コンパイルするようにします。
こちらの記事でも紹介されております。
Introducing Webpacker

まずはforemanのGemをインストールします。
foremanProcfileから複数のプロセスを管理することができます。
https://github.com/ddollar/foreman

これでbundle installします。
次に下記2点のファイルを作成します。

ファイル名 役割
bin/server Procfile.devのコマンドを実行する
Procfile.dev rails sbin/webpack-dev-serverを実行する

また、bin/serverのパーミッションを変更しておきます。

これでbin/serverを実行してみると、http://localhost:5000で起動します。
(ポート番号が変わります。)
ここで、app.vueの下記の部分を変えて保存すると、コンパイル処理が走り、画面がリロードされるはずです。
適当に変えて遊んでみて、問題なさそうなら大丈夫です。

APIの準備

サーバーサイドのAPI部分を実装していきます。

テーブルとモデルの生成

テーブル名はtasksとし、下記のようにします。

カラム
name VARCHAR(255) NULL: false
is_done BOOLEAN NULL: false, DEFAULT: false
created_at DATETIME NULL: false
updated_at DATETIME NULL: false

マイグレーションファイルとモデルはrails generateで作成してしまいます。

マイグレーションファイルにnull: falsedefault: falseを追記するため、下記のように編集します。

マイグレーションを実行します。

ついでに、モデルのnameプロパティにバリデーションをつけておきましょう。

コントローラーと返却するJSONファイルを生成

アクセスするURLは/api/tasksのように名前空間を切りたいと思います。
まずはルーティングからです。

次にコントローラーを作成します。
コントローラーはapp/controllersの中にapiというディレクトリを作成し、そこに作ります。

ビューのJSONファイルも同様に、views/api/tasks以下に作成します。

seeds.rbを作成し、curlコマンドで確認

seeds.rbを作成し初期データを作成できるようにします。
もしレコードが増えすぎてもこれでやり直しもできます。

DBに適用します。

なお、リセットする場合は、

curlコマンドを使用してAPIの確認をします。

次にPOSTで新規作成してみます。
ここで恐らくエラーになるかと思います。

log/development.log を確認すると、ActionController::InvalidAuthenticityTokenというエラーのようです。
CSRF対策のトークンがないため、Railsから怒られてしまいます。

application_controller.rbの下記をコメントアウトするとエラーが出なくなります。
本来であれば、API認証のようなものをつけた方が良いとは思いますが、今回は割愛します。
【Rails】RailsでAPIの簡単なトークン認証を実装する

これで作成されたTODOが返却されるかと思います。

Materializeの導入

CSSはMaterializeというフレームワークを使用しようと思います。
マテリアルデザインを意識したものになっており、個人的に使ってみたいと思っておりました。

Gemもすでにあるので、Rails側でインストールします。
https://github.com/mkhairi/materialize-sass

bundle installした後、下記の2ファイルに追記します。
cssscssに変更しておきます。

$primary-color$secondary-colorを設定しておくことで、よしなに色を合わせてくれます。
カラースキームはこちらを参照してください。

コンポーネントを使ってヘッダーを作成

ここからVue.jsを中心に画面を作っていきます。
まずはヘッダーを作成します。

元となるビューファイルはRailsのindex.html.erbになるので、こちらにVue.jsを載せられるようにします。

<navbar>というタグがありますが、Vue.js側でこのタグとコンポーネントを紐付け、表示します。
コンポーネント
また、新しくtodo.jsというファイルをapp/javascript/packsに作成します。
hello_vueを修正したも良いのですが。。。)

ここで、import Vue from 'vue/dist/vue.esm.js'としています。
これは、後ほどコンポーネントを使用する際に完全ビルドする必要があるからだそうです。
(すいません、まだよく分かっていません。。。)
詳しくはこちらの方が解説記事を書いてくださっています。
Rails5.1でVue.jsで単一ファイルコンポーネントのエラーがでる

これでindex.html.erb内の<div id="app">にマウントされます。
このまま実行しても特に何もありません。

それではコンポーネントを作成します。
packsの下にcomponentsディレクトリを作成して、そこにheader.vueを作成します。
コンポーネントは.vueで作成します。

これをtodo.jsに登録します。

navbarという名前でコンポーネントとして登録します。
headerだと<header>タグがすでにHTML5に存在しているため。)
これで<navbar>タグが使用できるようになりました。

サーバーを再起動してアクセスすると下図のようなヘッダーができているのではないでしょうか?

Vue-Routerを使用してSPAっぽく

Vue-Routerを使用することで、登録されたパスとコンポーネントで画面内を差し替えることができます。
Vue-Router

yarnを使ってvue-routerを追加します。

今回は「TODO一覧(メイン画面)」、「アバウト(おまけ)」、「コンタクト(おまけ)」を用意します。
正直、メイン画面でTODO管理はできるので、あと2つはおまけです。

まずはコンポーネントを作成します。

さて、このコンポーネントとパスを登録するrouter.jsを作成します。
こちらもrouterディレクトリを作成してそちらに作成します。

パスとコンポーネントを結びつけます。
また、mode: 'history'とすることで、HTMLのhistory APIを使用して、一見同じビュー内ですがURLを書き換えることができます。
HTML5 Historyモード

また、VueRouterを使用すると、<router-link><router-view>というタグが使用できます。
<router-link>は、<a>タグとして変換されますが、画面遷移ではなくVueRouterに登録されたパスからコンポーネントを探します。
そして<router-view>の部分に表示します。

ヘッダーの各リンクを修正します。

それぞれのコンポーネントが表示される部分をindex.html.erbに作ります。

最後に、todo.jsに追加します。

これで、ヘッダーの各リンクを押すと本文が切り替わるのではないでしょうか?
URLもHistoryモードのおかげで書き換わっています。

ただ、例えばhttp://localhost:5000/aboutでリロードすると、Rails側でエラーになってしまいます。
たしかにroutes.rbで登録していません。
とりあえず、/aboutでも/contactでもHome#indexにとぶよう記述します。

これでhttp://localhost:5000/aboutにアクセスするとわかりますが、ちゃんとURLからAboutのコンポーネントを表示してくれます。

Axiosを使ってAPI通信

axiosは、Ajax通信ライブラリです。
まずはこれをインストールします。

Axiosを使用してindex.vueにてAPI通信してタスク管理したいと思います。

まずは完成イメージをindex.vuetemplate内に記載します。
これを書き換えていきます。

こんな感じになります。

一覧表示

コンポーネントの中でHTML、JS、CSSをまとめて記載することを単一コンポーネントというようです。
.vueファイルの中で、そのコンポーネントで使うJSも記述することができます。
一覧をAPIで取得するために必要なものを追記します。

インスタンスにプロパティとしてtasksnewTaskを与えます。
メソッドとしてfetchTasksを登録し、APIで取得してきた値をループさせてtasksに格納します。
AxiosはJQueryと同じ感じで使えるので、使用しやすいかと思います。

mountedはVueインスタンスがマウントされたタイミングで実行されるライフサイクルフックです。
createdもあって今回の場合、あまり違いはありませんが、ライフサイクルダイアグラムについては、ちゃんと理解する必要がありそうです。

template内の下記の部分を書き換えます。
(未完了と完了済みを両方変えます。)
v-forv-ifを使ってtasksプロパティの中で条件に合うものを表示しています。
条件付きレンダリング
また、v-bindで強引にid名を作っています。

完了済みタスクを常に表示させておく必要はないと思います。
display_noneというクラスをつけているので、ここに非表示のスタイルをあてたいと思います。
単一コンポーネントではCSSも管理できます。

ただ、デフォルトの設定だと、コンパイル時に別でスタイルシートを出力してしまうので、これを無しにします。
RailsとVue.js の設計覚書

Webpack 3から、下記のようにloaders/vue.jsの中でextractCSS = falseと修正すれば良いようです。

index.vueにスタイルを追記します。
<style>タグ中にscopedという属性をつけておくと、そのファイルのみで有効なスタイルとして認識してくれます。
そのコンポーネントでしか使わないようなクラスはここで定義してしまえば良さそうです。

また、APIの返り値を使用してレンダリングする場合、API処理が終わるまでは、{{ task.name }}がそのまま文字列としてレンダリングされてしまいます。
(インスタンスに値がセットされたら、その値が表示されます。)
このとき、v-cloakというディレクティブをCSSのdisplay: none;と組み合わせて使うと、インスタンスが生成されたタイミングで表示してくれます。

これで完了済みの方は非表示になったかと思います。

ボタンを押すと、完了済みエリアが表示される

まずはVueのmethodsにボタンを押されたときのメソッドを登録します。

「ボタンを押されたとき」と記載するのはかなり簡潔にかけます。
テンプレートの方でv-onを使用してクリックされたタイミングでdisplayFinishedTasksを呼んでもらいます。

新規作成フォームをつくる

v-modelを利用することで、双方向バインディングさせることができます。
これで<input>タグで入力された値とインスタンスのnewTaskプロパティをバインドさせます。

次に新規作成のメソッドを追加します。
APIで新規作成できた場合は、tasksプロパティの先頭に追加するようにします。

チェックをつけたら完了済みに移す

まずはチェックボックスにチェックがついたら更新メソッドを呼ぶようにしたいと思います。
(この辺りは毎回呼ばずに、一定時間で同期させても良いかもしれません。)

更新用のメソッドを追加します。
更新したタイミングで、未完了部分から消し、完了済みの方に追加します。
(このあたりの処理はもっと良い書き方がある気がします。。。)

ここまでで、下図のような動きができるかと思います。
(いくつかスタイルはたしました。)

テストコードを書いてみる

試しにテストコードを書いてみました。
RailsのSystemTestCaseで書いております。
理由は、Node.jsのテストフレームワークは(私にとって)学習コストが高いためです。
本当はフロントエンドと分けるのであれば、テストも分けた方が良いのではないかと思っています。
https://github.com/naoki85/todo_app_with_vue_and_rails/commit/4a5225395fa2fe6ba9c7a3f8a421648ab28c897c

テストの中で、withinを使用していますが、今回の場合は使用する必要はないと思います。
今後、さらに詳細なテストをする際に使用するかもと思い、残したままにしてあります。

まだ記事にできるほど試していないため、参考程度にご覧ください。

さいごに

今回は、状態管理に便利なVuexは今回できなかったため、機会があればまた書きたいと思います。
もしよろしければご意見いただけると嬉しいです。
参考にさせていただいたドキュメント、記事などは下記になります。

blog-page_footer_336




blog-page_footer_336




-FrontEnd
-,

執筆者:

         

免責事項

このブログは、記事上部に記載のある投稿日時点の一般的な情報を提供するものであり、投資等の勧誘・法的・税務上の助言を提供するものではありません。仮想通貨の投資・損益計算は複雑であり、個々の取引状況や法律の変更によって異なる可能性があります。ブログに記載された情報は参考程度のものであり、特定の状況に基づいた行動の決定には専門家の助言を求めることをお勧めします。当ブログの情報に基づいた行動に関連して生じた損失やリスクについて、筆者は責任を負いかねます。最新の法律や税務情報を確認し、必要に応じて専門家に相談することをお勧めします。


  1. 匿名 より:

    extractCSS = falseの設定ですが、こちらの記述をloader/vue.jsにするのが正しいです。

    //コメントアウト
    // const extractCSS = !(inDevServer && (devServer && devServer.hmr)) || isProduction
    //これを追記
    const extractCSS = false

  2. 匿名2 より:

    とても参考になりました。ありがとうございます。

    補足ですが、index.vue の newTask ボタンに v-on:click=”createTask” の記載が漏れているようです。ご確認ください。

comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


関連記事

svelte

Svelteのチュートリアルをやってみた

1 はじめに2 Svelteとは?2.1 Svelteの特徴2.2 Write less code2.3 No Virtual DOM2.4 Truly reactive3 Svelteのシンタックス ...

Vue.js入門その2〜Vueインスタンスってなんぞ?〜

1 はじめに2 Vueインスタンス3 プロパティとメソッド4 インスタンスライフサイクルフック5 フィルター6 算出プロパティ6.1 例文6.2 算出プロパティを使用した書き換え6.2.1 HTML6 ...

Vue.js入門その8〜vue-draggableを使ってドラッグ機能の実装〜

1 はじめに2 準備3 一列の並び替え3.1 移動する前、移動した後3.1.1 move3.1.2 end4 2列間の移動4.1 片方を空配列にすると。。。5 さいごに6 おすすめ書籍 はじめに 久し ...

Vue.js入門その5〜マークダウンのリアルタイムプレビューをつくる〜

1 はじめに1.1 環境構築2 marked.jsの設定2.1 CDNのロード2.2 動作確認3 マークダウンを変換して表示3.1 雛形の作成3.1.1 HTML3.1.2 JS3.2 HTMLに変換 ...

Vue on Rails で作ったアプリを振り返ってみる

1 はじめに2 全体的なこと2.1 ディレクトリ構成2.2 CSSフレームワーク2.3 Capistranoでのデプロイ3 axiosのラッパー4 Vuex4.1 ログインの状態管理5 Router6 ...

フォロー

follow us in feedly

blog-page_side_responsive

2018年4月
1234567
891011121314
15161718192021
22232425262728
2930  

アプリ情報

私たちは無料アプリもリリースしています、ぜひご覧ください。 下記のアイコンから無料でダウンロードできます。