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/serverProcfile.devのコマンドを実行する
Procfile.devrails sbin/webpack-dev-serverを実行する

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

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

APIの準備

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

テーブルとモデルの生成

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

カラム
nameVARCHAR(255)NULL: false
is_doneBOOLEANNULL: false, DEFAULT: false
created_atDATETIMENULL: false
updated_atDATETIMENULL: 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は今回できなかったため、機会があればまた書きたいと思います。
もしよろしければご意見いただけると嬉しいです。
参考にさせていただいたドキュメント、記事などは下記になります。

page_footer_300rect




page_footer_300rect




-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


関連記事

Vue.js入門その1〜基本文法〜

はじめに 軽量JSフレームワークとして有名なVue.js。 最近、Laravelに触れる機会が増えたことと、以前からRails + Vueという構築を耳にするので、今更ではありますが勉強を始めようと思 ...

js

JavaScriptのみでPOSTした場合のCSRFトークンの認証に関するTIPS

1 はじめに2 実装2.1 なぜエラーが発生したか2.2 修正後のコード3 さいごに はじめに RailsアプリケーションでPOSTリクエストを送る場合、多くの場合はform_withなどのヘルパーを ...

JQueryでformにinput要素を足していく

はじめに 最近個人的にディープラーニングの勉強をしているtonnyです。 ディープラーニングの勉強がてら、好きな麻雀に関するWebアプリを作成してみました。 今回はその作成の中で、今まであまりやってこ ...

Vue.js入門その6〜RouterとComponentを使ってTODOアプリを修正〜

1 はじめに2 vue-routerのインストール3 サーバーサイドの改修3.1 APIに詳細(show)を追加3.2 元となるビューファイルを作成3.3 ルーティングの修正4 Vue.jsの実装4. ...

[Rails + Materialize] パンくずリスト用のヘルパーを作成した

1 はじめに2 パンくずリストを上書き2.1 サンプルのHTML2.2 CSSの上書き3 ヘルパーにする4 さいごに はじめに またまたMaterialize関連の記事になります。 Materiali ...

AppLink

page_side_300rect

アプリ情報

私たちは、目標を達成したい方を応援する、TODOアプリもリリースしております。
下記のアイコンから無料でダウンロードできます。

フォロー

follow us in feedly
2018年4月
« 3月 5月 »
1234567
891011121314
15161718192021
22232425262728
2930 

Web版MyCoach

私たちはより広い方にコーチングを知ってもらいたいと考えています。 下記のサイトにて、コーチの方々を紹介しておりますので、よろしければご覧ください。