All threads / Sorting Images using Active Storage
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.

I figured out how to do it. Thanks anyway.

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

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

I'm also interested. Thanks in advance

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

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.

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.

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!!!

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

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.

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.

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

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.

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

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.

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!

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">

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 %>

Check it out again Scott.

And......yet another question! Can you tell me how you got sortable js going? Do you have your own js controller or just add to application js? Getting noMethod error but realized I have not added to proj.

Make sure you have jQuery sortable enabled / installed in your code.

Seriously wanna stop pestering you but I am relatively new to the whole webpacker approach to js libraries. I can't seem to get this rolling. Could you share your js implementation of jquery-ui? The js file you added as well as any declarations you may have in application.js or elsewhere?

Hey Tony, checking in again if you can hook me up with your sortable implementation?

Look up jQuery sortable and make sure you have it installed. I do NOT use webpack. It's too complicated IMO for simple things.

Join the discussion

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

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

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

    logo Created with Sketch.

    Ruby on Rails tutorials, guides, and screencasts for web developers learning Ruby, Rails, Javascript, Turbolinks, Stimulus.js, Vue.js, and more. Icons by Icons8

    © 2021 GoRails, LLC. All rights reserved.