LoginSignup
7
6

More than 1 year has passed since last update.

Rails API + Vue.js のSPAにCarrierWaveを導入する

Last updated at Posted at 2021-06-08

はじめに

CarrierWaveの導入はRailsアプリケーションでは簡単にできるのですが、SPAでは少し苦労したのでまとめておきます。
本記事では、ローカル(public/uploader以下)にファイルをアップロードするデフォルトの設定で進めます。
また、CarrierWaveの基本的な説明は省略します。

環境

  • Ruby 3.0.0
  • Rails 6.1.3
  • Vue.js 2.6.12
  • Vue CLI 4.5.1

CarrierWaveのインストール

この記事ではJSONデータの作成にjbuilderを使うので、一緒にインストールしておきます。

Gemfile
gem 'carrierwave'
gem 'jbuilder'
$ bundle install

Railsの設定

$ rails g uploader Image

$ rails g model Post image:string

$ rails db:migrate
app/models/post.rb
mount_uploader :image, ImageUploader
config/routes.rb
Rails.application.routes.draw do
  scope format: 'json' do
    resources :posts
  end
end

app/uploaders/image_uploader.rbはデフォルト設定のまま使います。
ただし、このままファイルをアップロードしてVue.js側で取得するとURLは
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"となり表示できません。
なので、config/initializers/carrierwave.rbを作成して、

config/initializers/carrierwave.rb
CarrierWave.configure do |config|
  config.asset_host = 'http://localhost:3000'
end

これで、"http://localhost:3000/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"となり表示できるようになります。

続いて、Postsコントローラーを作ります。

app/controllers/posts_controller.rb
class PostsController < ApplicationController

  def index
    posts = Post.all
    @images = posts.map { |post| post.image.url }
  end

  def create
    @post = Post.new(post_params)
    if @post.save
      render json: :created
    else
      render json: @post.errors, status: :unprocessable_entity
    end
  end

  private

  def post_params
    params.require(:post).permit(:image)
  end
end

ポイントはpost.image.urlです。
Rails側でurlまで取得しておかないと、Vue.jsで表示したときにconsoleに警告が出ます。

app/views/posts/index.json.jbuilderを作成してRails側は完了です。

app/views/posts/index.json.jbuilder
json.images do
  json.array! @images
end

Vue.jsの設定

まずは、投稿フォームを作ります。
<input type="file">ではv-modelは使えないので、@￰changeでファイルを取得します。

PostsNew.vue
<template>
  <div>
    <input type="file" @change="setImage">
    <button @click="postImage">
  </div>
</template>

<script>
import axios from '@/axios'

export default {
  data () {
    return {
      image: ''
  },
  methods: {
    setImage (e) {
      this.image = e.target.files[0]
    },
    postImage () {
      const formData = new FormData()
      formData.append('post[image]', this.image)
      axios.post('/posts', formData)
    }
  }
}
</script>

Rails側でStrong Parametersを設定しているので、パラメータを

{"post" => {"image"=>"xxxxx/xxxx/xxxx"}}

で渡すために、formData.append('post[image]', this.image)としています。

最後に投稿一覧を作成します。

PostsIndex.vue
<template>
  <div>
    <div v-for="(image, index) in images" :key="index">
      <img :src="image">
    </div>
  </div>
</template>

<script>
import axios from '@/axios'

export default {
  data () {
    return {
      images: []
  },
  created () {
      axios.get('/posts').then(response => { this.images = response.data.images })
  }
}
</script>
7
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6