How do I upload files with ActiveStorage and Vue.js?
Hey GoRailers,
Stemming from the Rails & Vue.js Trello Clone, I'm looking to add file uploads to a Card model using the new Rails 5.2 ActiveStorage feature. I'm stronger in Rails - previously I would use Paperclip to handle this. I've setup the config/storage.yml
file and necessary migrations for ActiveStorage. I've also set the association between on the Card and updated the CardController
to permit files: []
.
The card.vue
component built from the tutorial currently works well; a Card can have a title and description, and the description can be updated. These records persist to the database. My problem is figuring out how to bind file uploads to the card, and the logic required to upload multiple files on save.
Currently I'm using <input name="files" type="file" data-direct-upload-url="/rails/active_storage/direct_uploads" direct_upload="true" />
to create the input field in the card. However, after selecting a PDF image from my local machine and clicking save, the logs show that nothing happens [in regards to inserting a new row in active_storage_attachments or creating a blob]. How can I expand the save method to accept the file? Is the input
field sufficient to bind the element?
card.rb
class Card < ApplicationRecord
has_many_attached :files
end
CardController
class CardsController < ApplicationController
private
def card_params
params.require(:card).permit(:list_id, :title, :position, :description, files: [])
end
end
card.vue
<template>
<div>
<div @click="editing=true" class="card card-body">
<h4>
{{card.title}}
</h4>
</div>
<div v-if="editing" class="modal-backdrop show"></div>
<div v-if="editing" @click="closeModal" class="modal show" style="display: block">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<div>
<h4>
{{card.title}}
</h4>
</div>
</div>
<div class="modal-body">
<div>
<h5>{{card.description}}</h5>
</div>
<textarea v-model="description" class="form-control"></textarea>
</div>
<div class="modal-footer">
<input name="files" type="file" data-direct-upload-url="/rails/active_storage/direct_uploads" direct_upload="true" />
<button @click="save" type="button" class="button button-secondary">Save changes</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ["card", "list"],
data: function() {
return {
editing: false,
title: this.card.title,
description: this.card.description,
files: []
}
},
methods: {
save: function() {
var data = new FormData
data.append("card[title]", this.title)
data.append("card[description]", this.description)
Rails.ajax({
url: `/cards/${this.card.id}`,
type: "PATCH",
data: data,
dataType: "json",
success: (data) => {
const list_index = window.store.lists.findIndex((item) => item.id == this.list.id)
const card_index = window.store.lists[list_index].cards.findIndex((item) => item.id == this.card.id)
window.store.lists[list_index].cards.splice(card_index, 1, data)
this.editing = false
}
})
}
}
}
</script>
Has anyone had any success with Vue and ActiveStorage in the past? I could really use some assistance in refactoring the form to handle file uploads.
I think it should be FormData() and not FormData. By just using FormData...it will return you the function.
You need to go through this documentation.
Direct Uploads in Rails
The thing which is missing from the code is that you are just sending the title and description to your PATCH method in the cards controller. You are not sending any file related things like the signed ID for the blob of the file which you uploaded to the cloud server. Hence the database is not showing any uploads related information.
You should look at this discussion which might be helpful in understanding how direct upload works