Skip to content

How to: Add more files and remove single file when using default multiple file uploads feature

Charles Jan edited this page Dec 12, 2023 · 4 revisions

You could use PostgreSQL array to implement such a feature. To be specific, in your migration, you could write

class AddImagesToGallery < ActiveRecord::Migration
  def change
    add_column :galleries, :images, :string, array: true, default: [] # add images column as array
  end
end

In your view, you could do this

.field 
  = f.file_field :images, multiple: true # make this input a multiple files input  

In your controller, you could allow nested params in the following way

def gallery_params
  params.require(:gallery).permit(:title, {images: []}) # allow nested params as array
end

When you want to implement add more and remove single image feature, you could write your routes in the following way

Rails.application.routes.draw do
  resources :galleries do
    resources :images, :only => [:create, :destroy] # support #create and #destroy
  end
end

So that you could implement your views in the following way

h1 Add more images

= form_for @gallery, url: gallery_images_path(@gallery), method: :post do |f| # use customized url endpoint 
  .field
    = f.file_field :images, multiple: true

  .actions = f.submit "Add More Images"

And

div
  - @gallery.images.each_with_index do |image, index| #grab the index
    div 
      = image_tag(image.url)
      = link_to "Delete", gallery_image_path(@gallery, index), :method => :delete, data: { confirm: "Are you sure you want to delete this image?" }

So that you could implement your image controller in the following way

class ImagesController < ApplicationController
  before_action :set_gallery

  def create
    add_more_images(images_params[:images])
    flash[:error] = "Failed uploading images" unless @gallery.save
    redirect_to :back
  end

  def destroy
    remove_image_at_index(params[:id].to_i)
    flash[:error] = "Failed deleting image" unless @gallery.save
    redirect_to :back
  end

  private

  def set_gallery
    @gallery = Gallery.find(params[:gallery_id])
  end

  def add_more_images(new_images)
    images = @gallery.images
    # If you don't want to re-upload existing files, you should use
    # `@gallery.images = images.map(&:identifier) + new_images` 
    # instead of the following statement
    images += new_images
    @gallery.images = images
  end

  def remove_image_at_index(index)
     remain_images = @gallery.images
     if index == 0 && @gallery.images.size == 1
       @gallery.remove_images!
     else
       deleted_image = remain_images.delete_at(index) 
       deleted_image.try(:remove!)
       # Also,you need to use `@gallery.images = remain_images.map(&:identifier)` 
       # to prevents re-uploading existing files
       @gallery.images = remain_images
     end
  end

  def images_params
    params.require(:gallery).permit({images: []}) # allow nested params as array
  end
end

For more detailed step by step walkthrough, please refer to Multiple Images Uploading With CarrierWave and PostgreSQL Array

Clone this wiki locally