前回:ヘッダー・フッターをコンポーネントに分割 - 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)); } } . . .