APIでデータを取得してVueで呼び出す

前回:ヘッダー・フッターをコンポーネントに分割 - Rails技術ブログ

①モデルを作成。
$ bundle exec rails g model Task title:string
class CreateTasks < ActiveRecord::Migration[6.0]
  def change
    create_table :tasks do |t|
      t.string :title, null: false

      t.timestamps
    end
  end
end

models/task.rb

class Task < ApplicationRecord
  validates :title, presence: true
end
$ bundle exec rails db:migrate


②コントローラーを作成。
$ bundle exec rails g controller Api::Tasks index show create update destroy --skip-routes

app/controllers/api/tasks_controller.rb

class Api::TasksController < ApplicationController
  before_action :set_task, only: [:show, :update, :destroy]

  def index
    @tasks = Task.all
    render json: @tasks
  end

  def show
    render json: @task
  end

  def create
    @task = Task.new(task_params)

    if @task.save
      render json: @task
    else
      render json: @task.errors, status: :bad_request
    end
  end

  def update
    if @task.update(task_params)
      render json: @task
    else
      render json: @task.errors, status: :bad_request
    end
  end

  def destroy
    @task.destroy!
    render json: @task
  end

  private
  
  def set_task
    @task = Task.find(params[:id])
  end

  def task_params
    params.require(:task).permit(:title)
  end
end


③ルーティングを設定。
Rails.application.routes.draw do
  root to: 'home#index'

  namespace :api, format: 'json' do
    resources :tasks
  end

  get '*path', to: 'home#index'
end


CSRF対策を無効化。

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  protect_from_forgery with: :null_session
end

これがないとPOSTができない。

curlコマンドでデータをタスクを作成。
$ curl -X POST -H "Content-Type: application/json" -d '{"title":"Rubyのサンプルコードを書く"}' localhost:3000/api/tasks

確認

$ curl http://localhost:3000/api/tasks


⑥axiosをインストール。
$ yarn add axios


⑦hello_vue.jsにaxiosをインポート。
import Vue from 'vue'
import App from '../app.vue'
import router from '../router'
import axios from 'axios'
import 'bootstrap/dist/css/bootstrap.css'

Vue.config.productionTip = false

// インスタンスプロパティの追加。
// これにより各コンポーネントでインポート処理を書かなくても"this.$axios.get~~~"の形でaxiosを使える。
Vue.prototype.$axios = axios

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
    router,
    render: h => h(App)
  }).$mount()
  document.body.appendChild(app.$el)
})


⑧データを読み込みたいvueファイルを編集。
<template>
  <div>
    <div class="d-flex">
      <div class="col-4 bg-light rounded shadow m-3 p-3">
        <div class="h4">TODO</div>
        <div v-for="task in tasks" :key="task.id" class="bg-white border shadow-sm rounded my-2 p-4">
          <span>{{ task.title }}</span>
        </div>
      </div>
    </div>
    <div class="text-center">
      <router-link to="/" class="btn btn-dark mt-5">戻る</router-link>
    </div>
  </div>
</template>

<script>
export default {
  name: 'TaskIndex',
  data() {
    return {
      tasks: []
    }
  },
  // 初期化の過程において、特定の状態になったタイミングで自動的に呼び出される関数をライフサイクルフックと言う。
  // createdフックにはVueインスタンスが生成された後に実行したい処理を記述する。
  created() {
    this.fetchTasks();
  },
  // Vueでメソッドを定義する際はmethodsというキーに対して記述する。
  methods: {
    fetchTasks() {
      this.$axios.get("https://localhost:3000/api/tasks")
        .then(res => this.tasks = res.data)
        .catch(err => console.log(err.status));
    }
  }
}
</script>

<style scoped>
</style>


⑨baseURLを設定。
$ mkdir app/javascript/plugins
$ touch app/javascript/plugins/axios.js
import axios from 'axios'

const axiosInstance = axios.create({
  baseURL: 'api'
})

export default axiosInstance

baseURL: 'api'は本来baseURL: 'https://localhost:3000/api'だが省略できる。
これによりaxios.get()ではapi以降のURLを記載するだけで済み、さらに環境の違いによるURLの違いを気にしなくて済むようになる。

hello_vue.js

.
.
.
// import axios from 'axios'
// ↓
import axios from '../plugins/axios'
.
.
.

app/javascript/pages/task/index.vue

.
.
.
/*methods: {
  fetchTasks() {
    this.$axios.get("https://localhost:3000/api/tasks")
      .then(res => this.tasks = res.data)
      .catch(err => console.log(err.status));
   }
} */

// ↓

methods: {
  fetchTasks() {
    this.$axios.get("tasks")
      .then(res => this.tasks = res.data)
      .catch(err => console.log(err.status));
  }
}
.
.
.