Multiple image uploads with Carousel in Rails App - Stimulus

Published 11 April 2022

Tutorial
Multiple image uploads with Carousel in Rails App - Stimulus Cover Image

Carousels are a fantastic way of showcasing media in an app. For a long time, implementing this in Rails was rather difficult. Thanks to StimulusJS (which in my own words, gives Rails apps superpowers), we can easily build this feature out.
 
Please follow along to learn how to go about this.

First off, your resource needs to allow for multiple image uploads. Assuming you have a resource named Article, you would navigate to the article.rb model and include:
class Article < ApplicationRecord
  # Associations
  has_many_attached :photos

  # Optional validation
  validates :photos,
            blob: { content_type: %w[image/png image/jpg image/jpeg],
                    size_range: 0.1..5.megabytes }
end

You can read more on Active Record associations here.

After setting up the association, you will want to allow the resource attribute to be saved in the model. We do this in the articles_conttroller.rb:
def article_params 
	params.require(:post).permit(:title, :description, :photos => []) 
end

In the article form, you want to allow users to upload the multiple images by adding the following:
<div class="form-field"> 
	<%= form.label :photos %> 
	<%= form.file_field :photos, :multiple => true %> 
</div>

From there, we need to install Stimulus Carousel:
yarn add stimulus-carousel

In your app/javascript/controllers/index.js, you will need to import and register the component for it to work in your app: 
import Carousel from 'stimulus-carousel'
application.register('carousel', Carousel)

The carousel component uses SwiperJS underneath the hood. For this reason, you will have to import Swiper’s CSS in your app/javascript/packs/application.js. I personally have included the application.html.erb layout:
<script src="https://unpkg.com/swiper/swiper-bundle.min.js"></script>
<!-- Initialize Swiper -->
<script>
    varswiper= new Swiper('.swiper-container', {
        navigation: {
            nextEl: '.swiper-button-next',
            prevEl: '.swiper-button-prev',
        },
    });
</script>

I am not looking for much customization of the Swiper stuff.

When all this has been set up, you will need to navigate to the show (app/views/articles/show.html.erb ):
<% if @article.photos.attached? %>
  <div data-controller="carousel" class="swiper swiper-initialized swiper-horizontal swiper-pointer-events"
       data-carousel-options-value="{ &quot;navigation&quot;: { &quot;nextEl&quot;: &quot;.swiper-button-next&quot;, &quot;prevEl&quot;: &quot;.swiper-button-prev&quot; } }" >
    <div class="swiper-wrapper">
			<% @article.photos.each do |photo| %>
        <div class="swiper-slide swiper-slide-prev flex items-center justify-center" style="width: 992px;">
          <%= link_to url_for(photo) do %>
            <%= link_to url_for(photo) do %>
              <%= image_tag url_for(photo), :alt => "#{photo.filename}", :class => "w-fit flex justify-center items-center" %>
						<% end %>
					<% end %>
        </div>
			<% end %>
    </div>

	<%# Swiper Buttons %>
	<div class="swiper-button-next" tabindex="0" role="button" aria-label="Next slide" aria-controls="swiper-wrapper-3614d810d7f2d3fb5" aria-disabled="false"></div>
	<div class="swiper-button-prev" tabindex="0" role="button" aria-label="Previous slide" aria-controls="swiper-wrapper-3614d810d7f2d3fb5" aria-disabled="false"></div>
	<span class="swiper-notification" aria-live="assertive" aria-atomic="true"></span>
  </div>
<% end %>

And there you have it, a working carousel with multiple uploads.
carousel-result-1.mp4

We could improve this by allowing for looping of the images when the last image is loaded on the show view or even deletion of a single image from the array of images but that could be an improvement we tackle in another tutorial.

Thank you for following along and happy coding.

Tags:

Rails, Stimulus JS, ActiveStorage