I'm looking to add file uploads to a Card model (including single and multiple files) using the new Rails 5.2 ActiveStorage feature. I'm stronger in Rails and am using Vue.js to pull this off - previously I would use Paperclip to handle this. However, 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 that I created 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?
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>
Try to change your input name to fit with your permitted params:
<input name="card[files][]" type="file" ... />
Updated:
As I answered you need to change your params to fit with your permitted ones.
Now using Vue, you can remove the name
and add the files to the FormData on change event like this:
<template>
<div>
...
<div class="modal-footer">
<input ref="files" type="file" @change="onFilesChange"
data-direct-upload-url="/rails/active_storage/direct_uploads"
direct_upload="true" multiple />
<button @click="save" type="button" class="button button-secondary">Save changes</button>
</div>
...
</div>
</template>
<script>
export default {
props: ["card", "list"],
data: function() {
return {
editing: false,
title: this.card.title,
description: this.card.description,
form: new FormData
}
},
methods: {
onFilesChange: function() {
let files = this.$refs.files.files
for(let file of files) {
this.form.append('card[files][]', file)
}
},
save: function() {
this.form.append("card[title]", this.title)
this.form.append("card[description]", this.description)
Rails.ajax({
url: `/cards/${this.card.id}`,
type: "PATCH",
data: this.form,
dataType: "json",
success: (data) => {
// ...
this.editing = false
}
})
}
}
}
</script>
Note: don't forget the multiple
property on input if you want to upload multiple files.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With