Ask A Question

Notifications

You’re not receiving notifications from this thread.

Sorting Images using Active Storage

Tony Serkis asked in Rails

I'm experimenting with Active Storage to manage images. I got the multiple uploads working, but I want to use acts_as_list position with jQuery sortable to manage their order using ajax, like in the video on this site. In a normal model, I would run a migration to add position to the model. But it looks like I have no access to the Blob or Attachement models. How would I tackle this? Or do I have to go back to using Carrierwave? Any help would be appreciated.

Reply

I figured out how to do it. Thanks anyway.

Reply

And how did you do that ? A short explanation or a link would be appreciated :).

Reply

Hi Tony,
Can you provide me how you have implemented that?

Reply

I'm also interested. Thanks in advance

Reply

Sorry everyone. I didn't see your replies. IF you're still interested I can probably put something together.

Reply

Hey Tony! I am in the middle of tackling this EXACT issue and it's making me pretty crazy. Any chance you could share your solution? I am really struggling with getting acts_as_list into the attachments model, etc.

Reply

It took me a MONTH to figure this out. Sizing images, storage at AWS, sorting, etc. There was zero documentation. I'm pretty sure I was the first person to figure it out but it was only for me and my projects. I use this all the time in every project now. How far have you progressed?

Here's my really quick directions. Hope this helps.

Make sure you have jQuery installed.

You first have to get Active Storage installed.

rails active_storage:install
rails db:migrate

Add this for image processing and to use AWS.

gem 'image_processing', '~> 1.2'
gem 'aws-sdk-s3'

You need to add the position field to the model.

Here is the migration:

add_column :active_storage_attachments, :position, :integer
rails db:migrate

Add Acts as List gem.

gem 'acts_as_list'

Make sure you run: bundle install

I use this code to update the position of the images.

jQuery(document).ready(function($) {

$(".sort-me").sortable({
update: function(e, ui){
ui.placeholder.height(ui.item.height());
$.ajax({
url: $(this).data("url"),
type: "PATCH",
data: $(this).sortable('serialize')
});
}
})

});

These are my routes to sort and delete images. Example is a Product model.

resources :products, only: [:sort_attachments, :delete_image] do
collection do
patch '/sort/:id', action: :sort_attachments, as: 'sort_attachments'
delete '/:id/images/:image_id', action: 'delete_image', as: 'delete_image'
end
end

Lastly, the view. This is where Acts as List comes in. The code loops through the images for this Product and orders them by the Active Storage position field you added.

<% if product.persisted? %>
<% if product.images.present? %>



" class="ui-state-default image-box float-left">
<%= image_tag attachment.variant(combine_options: {auto_orient: true, thumbnail: '150x150', gravity: 'center', extent: '150x150' }), class: "image-fluid img-thumbnail" %>
<%= link_to delete_image_admin_products_path(product.id, attachment.id), method: :delete, class: "btn btn-xs btn-danger text-white", data: {confirm: "Are you sure?"} do %>

<% end %>

<% end %>



<% end %>
<% end %>

You'll probably have to add some CSS to make everything look nice. You can see the classes I used on the images.

I never had anyone help me (self-taught dev, still learning) and had to figure this out on my own. I'm glad to help as I wish I had someone to point me in the right direction a few year back.

Let me know how you make out.

Sorry to everyone else on this thread who I didn't reply to. This is the first notification I received since I posted this way back.

Reply

I am soooo close based on what you sent. And, yes, it is a freaking desert out there on this topic.

I pretty much have everything working except the URL call to do the re-ordering on the backend.

How did you manage to get acts_as_list added to the ActiveStorage::Attachment model?
In your example where does your "sort_attachments" method live?

Thank you SO MUCH for helping!!!

Reply

In my sample Product model, you need to add this: has_many_attached :images

Reply

To sort you have to have the position field added via the migration. Haha. It would help if I added the controller method.

def sort_attachments
params[:attachment].each_with_index do |id, index|
ActiveStorage::Attachment.where(id: id).update_all(position: index + 1)
end

head :ok
end

def delete_image
ActiveStorage::Attachment.where(id: params[:image_id])[0].purge
redirect_to edit_admin_product_path(params[:id]), notice: 'Image was successfully deleted.'
end

Also, make sure you PERMIT images: [] in your params. In this case I added to product_params in the controller.

Reply

Thank you...I understand all this. However, you are using acts_as_list, as am I. That gem needs to be declared in the model of the items that are being sorted -in this case the attachments. That's what exposes the position update method. My question is where did you declare acts_as_list? Which model? If in Product it would assume you were sorting Products and not attachments.

Reply

I do not have any references to acts_as_list in any model. Works without it.

Reply

Well that's great news! One last thing before I start hacking away. In your view, where is the url declaration for the sort_attachments ajax request? Sortable references it but I don't see in your view example.

Reply

I created a separate JS file called sorting.js and added in the jQuery code there.

Reply

If I'm understanding you correctly, I added the images at the bottom of my form edit in my admin. The path is referenced right in the loop of Product images.

Reply

I think it's just the view code you pasted got cut-off. It came through like this:

<% if product.persisted? %>
<% if product.images.present? %>

" class="ui-state-default image-box float-left">

Obviously a chunk missing!

Reply

This is what you sent before. But take a look - after your <% if product_images.present? %> it goes right to " class="ui-state-default image-box float-left"> which makes no sense. That is cut-off code. I would assume that missing bit before "class= is what I need. Sorry to be a pest! But the code that actually calls sort is where the magic will happen. Do you see that blank space on your end?

<% if product.persisted? %>
<% if product.images.present? %>

" class="ui-state-default image-box float-left">

Reply

Took me a minute to figure out the syntax of the code block.

<% if product.persisted? %>
      <% if product.images.present? %>
        <div class="row mb-3">
          <div class="col-md-12">
            <div class="sort-me mt-3" data-url="<%= sort_attachments_admin_products_path %>">
              <% product.images.order(:position).each do |attachment| %>
                <div id="<%= dom_id(attachment) %>" class="ui-state-default image-box float-left">
                  <%= image_tag attachment.variant(combine_options: {auto_orient: true, thumbnail: '150x150^', gravity: 'center', extent: '150x150' }), class: "image-fluid img-thumbnail" %>
                  <%= link_to delete_image_admin_products_path(product.id, attachment.id), method: :delete, class: "btn btn-xs btn-danger text-white", data: {confirm: "Are you sure?"} do %>
                    <i class="czi-trash"></i>
                  <% end %>
                </div>
              <% end %>
            </div>
          </div>
        </div>
      <% end %>
    <% end %>
Reply

Check it out again Scott.

Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 86,946+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.