5.1 + Vue.js で開発を行う - part2 Ajax で GET しよう!


概要

  • Rails 5.1 + Vue.js + Webpacker を使ってフロント開発を行う
  • 本記事では、Ajax 通信で、GET して Rails の View にいろいろ表示していきたい。
  • 全コードはこちらより確認いただけます

作るアプリ

  • 図書管理アプリを作ることにする
    • 本を保存したり、情報を参照するようなアプリを作る

開発の流れ

実際に開発する前に開発の流れを紹介しておきます。

  • Ajax で GET する方法を実装したいので、はじめに seed で DB に本の各種情報を保存しておきます
  • その後に、Vue を使って本の情報を取得したいと思います。

準備 | DB にデータを保存する

書籍のデータを作成していきたいと思います。

Book モデルの作成

Book モデルを作成します。
DB には、タイトル、筆者、出版社、ジャンルを保存しようと思います。

$ rails g model Book title:string author:string publisher:string genre:string
$ rake db:migrate

ダミーデータの作成


gem 'faker'

$ bundle install
seed.rb

20.times do
  Book.create(
    title: Faker::Book.title,
    author: Faker::Book.author,
    publisher: Faker::Book.publisher,
    genre: Faker::Book.genre
  )
end

$ rake db:seed

Rails View で表示する

Rails の View で先程の情報を表示します。

app/controllers/books_controller.rb

class BooksController < ApplicationController
  def index
    @books = Book.all
  end
end


app/views/books/index.html.haml

.container
  .row
    - @books.each do |book|
      .col.s4.m6
        .card
          %span.card-title
            = book.title

こんな感じで表示されます
(Materialize csss を使っていると...)

Ajax で本の情報を取得する

Ajax で情報を取得する際にどのように取得するのか考える必要がある。

選択肢は

  1. ページローディング時にすべての情報を取得する
  2. ページローディング後にユーザのアクションがあったときに情報を取得する
  1. ページローディング時にすべての情報を取得する

の方法は一番楽な手段だが、2 のほうが動作が軽く、ユーザが必要な情報だけを表示できる合理的な手段である。
そして、1 に関しては Rails で十分に表現できる範囲なので、今回はせっかくなので 2 で行いたいと思う。

具体的には、ページローディング時には、Rails 側で本のタイトルだけを表示しておき、 詳細 ボタンをユーザが押すと、Ajax で本の詳細情報を取得して表示する、みたいなことをやっていきたい。

API の作成

Ajax で GET しようと思うと、API を作成して、そこにリクエストを投げて JSON を取得することになります。
なので、はじめに API を作成しましょう。

各本の情報を取得するような API を作成していきます。
今回は jbuilder を使って API を作ろうと思います。
正直なところ、この程度のデータであれば jbuilder は不要なのですが複雑になってくると必要なので覚えておくと良かったりします。

routes

エンドポイントをわけるために、別のコントローラーを作成していきます。

routes.rb

Rails.application.routes.draw do
  root to: 'page#home'

  resources :books, only: %i(index)

+ namespace :api do
+   resources :books, only: %i(show)
+ end
end

controller

app/controllers/api/books_controller.rb
class Api::BooksController < ApplicationController
  def show
    @book = Book.find(params[:id])
    render 'show', formats: 'json', handlers: 'jbuilder'
  end
end

view

app/views/api/books/show.json.jbuilder
json.title     @book.title
json.author    @book.author
json.publisher @book.publisher
json.genre     @book.genre

json.extract! を使うほうがキレイだが、わかりやすくするために上記のようにした。

この状態で、ちゃんと json が返っているか確認しましょう。
api/books/1.json にアクセスしてみてください。以下のような表示になったら、json が返ってきている証拠です。

もし返ってきていなければ、コンソールにてネットワークを確認してください。
エラー内容が表示されているかもしれません。

以下のような json が返っていれば問題ないです。

Ajax で GET しよう!

Vue で Ajax 通信する際に便利なライブリーである axios をインストールします。

$ yarn add axios

本のタイトルをクリックしたときに、Ajax によって GET して Vue の data に値を代入することにします。
なので、card-title をクリックしたときに、Ajax で GET を行う setBookInfo メソッド(後ほど定義します)を実行します。

View

app/views/books/index.html.haml
-.container
+.container.js-booksIndex
  .row
    - @books.each do |book|
      .col.s4.m6
        .card
-         %span.card-title
+         %span.card-title{'v-on:click': "setBookInfo(#{book.id})"}
            = book.title

+ = javascript_pack_tag 'books/index'

Vue

el には 先程の View で指定した class を指定します。
methods には先程、説明しました setBookInfo() を定義していきます。
タイトルをクリックしたときに、id を取得するので、引数には id を入れておきます。

次に、axios を使って、get を行います。
すごく簡単です。get したい URL を以下のように指定するだけです。
あとは、返ってきた response を Vue の data に代入してあげます。

app/javascript/packs/books/index.js
import Vue from 'vue/dist/vue.esm';
import axios from 'axios';

new Vue({
  el: '.js-booksIndex',
  data: {
    bookInfo: {},
  },
  methods: {
    setBookInfo(id){
      axios.get(`api/books/${id}.json`)
        .then(res => {
          this.bookInfo = res.data;
        });
    }
  }
});

GET したデータを Rails View に表示する

タイトルをクリックしたら表示するようにしようと思います。

.container.js-booksIndex
  .row
    - @books.each do |book|
      .col.s4.m6
        .card
          %span.card-title{'v-on:click': "setBookInfo(#{book.id})"}
            = book.title

+  .row{'v-show': 'bookInfoBool'}
+    .col.s12.m12
+      .card.blue-grey.darken-1
+        .card-content.white-text
+          %span.card-title
+            {{ bookInfo.title }}
+          .detail
+            {{ bookInfo.author }}
+          .detail
+            {{ bookInfo.publisher }}
+          .detail
+            {{ bookInfo.genre }}

= javascript_pack_tag 'books/index'

表示条件をコントロールする bookInfoBool を定義します。
Rails の View 側にて、 {'v-show': 'bookInfoBool'} というふうにしておくと、 bookInfoBool == true の場合のみ表示されるようになります。

app/javascript/packs/books/index.js
import Vue from 'vue/dist/vue.esm';
import axios from 'axios';

new Vue({
  el: '.js-booksIndex',
  data: {
    bookInfo: {},
+   bookInfoBool: false
  },
  methods: {
    setBookInfo(id){
      axios.get(`api/books/${id}.json`)
        .then(res => {
          this.bookInfo = res.data;
+         this.bookInfoBool = true;
        });
    }
  }
});

Ajax で Get できれば表示されるので、get が成功したときに bookInfoBool = true にします。

以上のコードを追加すると、以下のようになると思います。