Im trying to use an image carousel in my Rails 5 app. I'm trying to follow the approach in this article: https://www.toptal.com/ruby-on-rails/rails-helper-bootstrap-carousel
I have an models for proposal and image. The associations are:
Proposal
has_many :images, as: :imageable
accepts_nested_attributes_for :images, reject_if: :all_blank, allow_destroy: true
Image
belongs_to :imageable, :polymorphic => true, optional: true
My view partial has:
<!-- GALLERY -->
<div id="myCarousel" class="carousel slide" data-ride="carousel">
<!-- <div class="carousel-inner" role="listbox"> -->
<% #images.each do |gallery| %>
<% if gallery == #images.order(created_at: :asc).first%>
<div class="item active">
<%= carousel_for(gallery) %>
<%= image_tag(gallery.picture.url, :class => "carousel_image", :style => "text-align: center" ) %>
<div class="carousel_caption">
<p style="text-align: center"><%= gallery.comment.humanize %></p>
<p style="text-align: center"><%= gallery.credit %></p>
</div>
</div>
<% else %>
<div class="item">
<%= carousel_for(gallery) %>
<%= image_tag(gallery.picture, :class => "carousel_image") %>
<div class="carousel_caption">
<p style="text-align: center"><%= gallery.comment.humanize %></p>
<p style="text-align: center"><%= gallery.credit %></p>
</div>
</div>
<% end %>
<% end %>
<!-- </div> -->
<a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="right carousel-control" href="#myCarousel" role="button" data-slide="next">
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
<!-- /GALLERY -->
Helper has:
module CarouselHelper
def carousel_for(images)
Carousel.new(self, images).html
end
class Carousel
def initialize(view, images)
#view, #images = view, images
#uid = SecureRandom.hex(6)
end
def html
content = safe_join([indicators, slides, controls])
content_tag(:div, content, id: uid, class: 'carousel slide')
end
private
attr_accessor :view, :images, :uid
delegate :link_to, :content_tag, :image_tag, :safe_join, to: :view
def indicators
items = images.count.times.map { |index| indicator_tag(index) }
content_tag(:ol, safe_join(items), class: 'carousel-indicators')
end
def indicator_tag(index)
options = {
class: (index.zero? ? 'active' : ''),
data: {
target: uid,
slide_to: index
}
}
content_tag(:li, '', options)
end
def slides
items = images.map.with_index { |image, index| slide_tag(image, index.zero?) }
content_tag(:div, safe_join(items), class: 'carousel-inner')
end
def slide_tag(image, is_active)
options = {
class: (is_active ? 'item active' : 'item'),
}
content_tag(:div, image_tag(image), options)
end
def controls
safe_join([control_tag('left'), control_tag('right')])
end
def control_tag(direction)
options = {
class: "#{direction} carousel-control",
data: { slide: direction == 'left' ? 'prev' : 'next' }
}
icon = content_tag(:i, '', class: "glyphicon glyphicon-chevron-#{direction}")
control = link_to(icon, "##{uid}", options)
end
end
end
My application.js has:
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require jquery-fileupload/vendor/jquery.ui.widget
//= require jquery-fileupload/jquery.iframe-transport
//= require jquery-fileupload/jquery.fileupload
//= require bootstrap-sprockets
//= require moment
//= require bootstrap-datetimepicker
//= require pickers
//= require underscore
//= require gmaps/google
//= require markerclusterer
//= require cocoon
//= require_tree .
When I try this, I get an error that says:
undefined method `count' for #<Image:0x007fb10c88bea8>
It highlights this line of the carousel (indicators method):
items = images.count.times.map { |index| indicator_tag(index) }
ARUN'S SUGGESTION
<div id="myCarousel" class="carousel slide" data-ride="carousel">
<!-- <div class="carousel-inner" role="listbox"> -->
<%# #images.each do |gallery| %>
<%# if gallery == #images.order(created_at: :asc).first%>
<div class="item active">
<%= carousel_for(#images) %>
<%= image_tag(#image.picture.url, :class => "carousel_image", :style => "text-align: center" ) %>
<div class="carousel_caption">
<p style="text-align: center"><%= #image.comment.humanize %></p>
<p style="text-align: center"><%= #image.credit %></p>
</div>
</div>
<%# else %>
<div class="item">
<%= carousel_for(#images) %>
<%= image_tag(#image.picture, :class => "carousel_image") %>
<div class="carousel_caption">
<p style="text-align: center"><%= #image.comment.humanize %></p>
<p style="text-align: center"><%= #image.credit %></p>
</div>
</div>
<%# end %>
<%# end %>
<!-- </div> -->
<a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="right carousel-control" href="#myCarousel" role="button" data-slide="next">
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
<!-- /GALLERY -->
ERROR SAYS: undefined method `picture' for nil:NilClass
2nd attempt at Arun's suggestion
<div id="myCarousel" class="carousel slide" data-ride="carousel">
<div class="item active">
<%= carousel_for(#images) %>
<%= image_tag(#image.picture.url, :class => "carousel_image", :style => "text-align: center" ) %>
<div class="carousel_caption">
<p style="text-align: center"><%= #image.comment.humanize %></p>
<p style="text-align: center"><%= #image.credit %></p>
</div>
</div>
<div class="item">
<%= carousel_for(#images) %>
<%= image_tag(#image.picture.url, :class => "carousel_image") %>
<div class="carousel_caption">
<p style="text-align: center"><%= #image.comment.humanize %></p>
<p style="text-align: center"><%= #image.credit %></p>
</div>
</div>
<!-- </div> -->
<a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="right carousel-control" href="#myCarousel" role="button" data-slide="next">
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
<!-- /GALLERY -->
Error says: undefined method `picture' for nil:NilClass
Arun's revised suggestion
Taking Arun's revised suggestion, I try replacing the entire gallery view, with:
<%= carousel_for(Image.all.map(&:picture_url)) %>
When i try to render the page, the first image is rendered. I can see from the chrome inspector, that the second image is recognised, although the carousel doesnt work. The chevrons don't move the images along. There are no js issues showing in the console.
You need to execute your javascript and jquery for this carousel to work properly.
In the end of your html file, add this code (it can be before the /body):
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<script>
$( document ).ready(function(){
$('#mycarousel').carousel();
});
</script>
You can also create a partial with this code and call it on the end of your application layout.
The problem is with the way you're calling carousel_for method. You should be passing an array of images but you're passing a single instance of Image type as you're iterating over #images.
ie. images inside the Carousel class is not an ActiveRecord Relation. So, you can't call the count method. The error message clearly says that you're trying to invoke the count on an image instance.
So, you should pass #images to carousel_for. Your view should only contain
carousel_for(#images)
EDIT
You should actually pass the array of image URLs to the carousel_for method.
<%= carousel_for(Image.all.pluck(&:picture_url)) %>
Related
I'm working on project on Ruby on Rails 6 and trying to make modal carousel with bootstrap 4. It works fine with the first element of massive, but don't with other elements. Arrows right and left don't work
<div class="container">
<div class="row">
<% #division.galleries.each do |gallery| %>
<div class="col">
<%= link_to image_tag(gallery.image_url(:thumb).to_s), [gallery.division, gallery],
data: {toggle: 'modal', target: "#myModal-#{dom_id(gallery)}"}, id: "image-#{dom_id(gallery)}" %>
<p>
</div>
<!-- Modal -->
<div class="modal fade" id="myModal-<%= dom_id(gallery) %>" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-body">
<div id="carouselExampleControls" class="carousel slide" data-ride="carousel">
<div class="carousel-inner">
<div class="carousel-item active">
<%= image_tag #division.galleries.find(gallery.id).image_url.to_s, class: "gallery1" %>
</div>
<% #division.galleries.drop(1).each do |gallery| %>
<div class="carousel-item">
<%= image_tag gallery.image_url.to_s, class: "gallery1" %>
</div>
<% end %>
</div>
<a class="carousel-control-prev" href="#carouselExampleControls" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next" href="#carouselExampleControls" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
</div>
</div>
</div>
</div>
<% end %>
</div>
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
import 'bootstrap'
import './stylesheets/application'
Hi I have a small app that shares a png of an image using img kit. These images are for social, so i would like to create multiple image versions for example a 1080 X 1080 version for instagram & a 1240 X 644 for LinkedIn.
The question I am asking is how do i create multiple images for the same view with imgkit
currently the images are created in the controller
def show
respond_to do |format|
format .html
format.png do
kit = IMGKit.new render_to_string, width: 1080, height: 1080
send_data kit.to_png, type: "image/png", disposition: "inline"
end
end
end
and accessed in the view via a share bar
<div class="row d-flex justify-content-center mb-1 mr-lg-5 ml-lg-5 pr-lg-5 pl-lg-5">
<div class="col d-flex justify-content-center align-items-center pt-3 border-dark border-top border-left border-bottom">
<p class="text-justify text-center">SHARE:</p>
</div>
<div class="col facebookshare d-flex justify-content-center align-items-center p-0 m-0 border-dark border-top border-bottom">
<%= link_to "https://www.pinterest.com/pin/create/button/" do %>
<i class="fab fa-facebook-f fa-2x d-none d-sm-block"></i>
<i class="fab fa-facebook-f d-sm-none d-md-none d-lg-none d-xl-none"></i>
<% end %>
</div>
<div class="col instashare d-flex justify-content-center align-items-center p-0 m-0 border-dark border-top border-bottom">
<%= link_to "https://www.pinterest.com/pin/create/button/" do %>
<i class="fab fa-instagram fa-2x d-none d-sm-block"></i>
<i class="fab fa-instagram d-sm-none d-md-none d-lg-none d-xl-none"></i>
<% end %>
</div>
<div class="col twittershare d-flex justify-content-center align-items-center p-0 m-0 border-dark border-top border-bottom">
<%= link_to "https://twitter.com/share?ref_src=twsrc%5Etfw" do %>
<i class="fab fa-twitter fa-2x d-none d-sm-block"></i>
<i class="fab fa-twitter d-sm-none d-md-none d-lg-none d-xl-none"></i>
<% end %>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</div>
<div class="col pinteresrshare d-flex justify-content-center align-items-center p-0 m-0 border-dark border-top border-bottom">
<%= link_to("https://www.pinterest.com/pin/create/button/") do %>
<i class="fab fa-pinterest-p fa-2x d-none d-sm-block"></i>
<i class="fab fa-pinterest d-sm-none d-md-none d-lg-none d-xl-none"></i>
<% end %>
<script async defer src="//assets.pinterest.com/js/pinit.js"></script>
</div>
<div class="col tumblrshare d-flex justify-content-center align-items-center p-0 m-0 border-dark border-top border-bottom">
<%= link_to("http://tumblr.com/widgets/share/tool?canonicalUrl=\#{quote_url(#quote)}", data: {
content: quote_url(#quote, format: 'png'),
caption: #quote.quote,
url: quote_url(#quote) })do %>
<i class="fab fa-tumblr fa-2x d-none d-sm-block"></i>
<i class="fab fa-tumblr d-sm-none d-md-none d-lg-none d-xl-none"></i>
<% end %>
<script id="tumblr-js" async src="https://assets.tumblr.com/share-button.js"></script>
</div>
<div class="col buffershare d-flex justify-content-center align-items-center p-0 m-0 border-dark border-top border-bottom">
<%= link_to("http://bufferapp.com/add", data: {
picture: quote_path(#quote, format: 'png'),
text: "this is the tweet text",
url: quote_path(#quote),
via: "#motiv8kit"}) do %>
<i class="fab fa-buffer fa-2x d-none d-sm-block"></i>
<i class="fab fa-buffer d-sm-none d-md-none d-lg-none d-xl-none"></i>
<% end %>
<script type="text/javascript" src="https://static.buffer.com/js/button.js"></script>
</div>
<div class="col dload d-flex justify-content-center align-items-center p-0 m-0 border-dark border-top border-bottom border-right">
<%= link_to quote_path(#quote, format: "png"), download: "" do %>
<i class="fas fa-download fa-2x d-none d-sm-block"></i>
<i class="fas fa-download d-sm-none d-md-none d-lg-none d-xl-none"></i>
<% end %>
</div>
</div>
quote_path is a named route for show, right? If so, then add the size as a param:
class MyClass
SIZES = {'linkedin' => [1240, 644], 'instagram' => [1080, 1080]}
def show
respond_to do |format|
format .html
format.png do
w, h = SIZES[params[:s]] # Remember to check presence of :s
# and set default values
kit = IMGKit.new render_to_string, width: w, height: h
send_data kit.to_png, type: "image/png", disposition: "inline"
end
end
end
end
In your view (adapt link in any case):
<%= link_to quote_path(#quote, format: "png", s: 'linkeding') do %>
<% end %>
<%= link_to quote_path(#quote, format: "png", s: 'instagram') do %>
<% end %>
I have a navbar where I display the MainCategory (Men, Women, Neutral)
(MainCategory is a model that have only one attribute :gender.)
I've definied the different genders like so:
enum gender: [:men, :women, :neutral]in my model.
Expected:
When I hover the link "Men" I want that the sub menu of "Men" shows up , same for "Women" and "Neutral".
Category is another model which has attributes :gender and a :title
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mx-auto">
<% MainCategory.order('gender ASC').all.each do |main_cat| %>
<li class="nav-link gender"><%= main_cat.gender %>
<div id="categories">
<ul class="navbar-nav mx-auto">
<% Category.where(gender: main_cat.gender).order('title ASC').each do |cat| %>
<% if cat.product_with_stock %>
<li class="nav-link"><%= link_to "#{cat.title} ", clients_category_path(cat), class: "nav-link" %></li>
<% end %>
<% end %>
</ul>
</div>
<% end %>
</li>
</ul>
</div>
</nav>
This the js file that show and hide the #category div.
$(document).on('turbolinks:load', function() {
$(".gender").mouseenter(function(){
$("#categories").slideDown();
})
$("body").click(function() {
$("#categories").slideUp();
});
$("#categories").click(function(e) {
e.stopPropagation();
});
});
With this code, I see the same submenu if I hover men or women, or neutral...
How should I improve this to have what expected?
You'll need to specify the related gender for each div and open it from your JS:
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mx-auto">
<% MainCategory.order('gender ASC').all.each do |main_cat| %>
<li class="nav-link gender" data-gender="<%= main_cat.gender %>"><%= main_cat.gender %>
<div class="categories" data-gender="<%= main_cat.gender %>">
<ul class="navbar-nav mx-auto">
<% Category.where(gender: main_cat.gender).order('title ASC').each do |cat| %>
<% if cat.product_with_stock %>
<li class="nav-link"><%= link_to "#{cat.title} ", clients_category_path(cat), class: "nav-link" %></li>
<% end %>
<% end %>
</ul>
</div>
<% end %>
</li>
</ul>
</div>
</nav>
JS:
$(document).on('turbolinks:load', function() {
$(".gender").mouseenter(function(){
var gender = $(this).data("gender")
$(".categories[data-gender=" + gender + "]").slideDown();
})
$("body").click(function() {
$(".categories").each(function() { $(this).slideUp() })
});
$(".categories").click(function(e) {
e.stopPropagation();
});
});
I can't test it, but this should work
Collapse is only working on the first button, and I know it's because only the first div id is being set for all buttons. How do I iterate and make new div id's for each element when using hash.each?
Here's my erb:
<% #contacts.each do |category, hash| %>
<div class="panel-group" id=accordion">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><%= category %></h3>
</div>
<div class="panel-body">
<ul class="list-group">
<% hash.each do |contact| %>
<li class="list-group-item" style="border: none">
<button type="button" class="btn btn-info" data-toggle="collapse" data-parent="#accordion" data-target="#demo"><%= contact['name'] %></button>
<div id="demo" class="panel-collapse collapse">
<div class="panel-body"><%= contact['email'] %></br><%= contact['ext'] %>
</div>
</div>
</li>
<% end %>
</ul>
</div>
</div>
</div>
<% end %>
Here's the corresponding code from my .rb:
get '/contact' do
contact = Contacts.new
#contacts = contact.getContacts
#contacts.each { |s| puts "get /contact found contact #{s.last.first['name']}" }
erb :contact
try
<% hash.each_with_index do |contact, index| %>
<li class="list-group-item" style="border: none">
<button type="button" class="btn btn-info" data-toggle="collapse" data-parent="#accordion" data-target="#collapse<%= index %>"><%= contact['name'] %></button>
<div id="collapse<%= index %>" class="panel-collapse collapse">
<div class="panel-body"><%= contact['email'] %></br><%= contact['ext'] %>
</div>
</div>
</li>
<% end %>
I used Ruby on Rails and I have created more of 40 hiraganas flashcards. In my index view, I do an iteration like this.
Some of Hiraganas cards have different I want to create different columns which host different color cards.
I want to iterate on letter A = green, E = red, I = Pink, etc
enter image description here
I only do that
<% #hiraganas.each do |hiragana| %>
<li>
<%= render 'allhiraganas', hiragana: hiragana %>
</li>
<% end %>
Do you know how to select only few elements to iterate. I see some tutos but I try .select
here is the render flashcards code
<div class="row">
<ul class="list-inline text-center card-frame">
<li>
<div class="card">
<div class="front">
<% if current_user.try(:admin?) %>
<%= link_to hiragana_path(hiragana), class:'trash-hiragana', data: { confirm: 'Are you sure?' }, method: :delete do %>
<%= image_tag("delete-btn.png") %>
<% end %>
<% end %>
<!-- PARAMETRER LE RESPONSIVE BOOTSTRAP -->
<!-- <div class="col-sm- col-xs-4 col-md-3"> -->
<span class="card-question popover-word" data-content="<h4 class='text-center letter-uppercase'><%= hiragana.bigletter.upcase %></h4><p class='text-center'><b><%= hiragana.midletter %></b> comme dans <b><%= hiragana.transcription %></b></p>">
<i class="fa fa-eye fa-lg"></i>
</span>
<!-- son de l'hiragana -->
<span class="audioclick popover-word" data-content="<p class='text-center'><b>le son arrive prochainement !</b></p>">
<i class="fa fa-volume-up fa-lg"></i>
</span>
<!-- image mnémotechnique -->
<span class="idea popover-word" data-content="<p class='text-center'><b>l'image est bientôt prête !</b><%= hiragana.upload %></p>">
<i class="fa fa-lightbulb-o fa-lg"></i>
</span>
<!-- <div class="prononciation">
<i class="fa fa-comment"></i>
</div> -->
<div class="card-hiragana hiragana-<%=hiragana.bigletter.downcase.last%>">
<h1><b><%= hiragana.ideo1 %></b></h1>
</div>
<div class="card-katakana">
<p><%= hiragana.ideo2 %></p>
</div>
<div id="favorite_<%=hiragana.id%>">
<%= render 'favs/favorites', hiragana: hiragana %>
</div>
</div>
<div class="back">
<div class="col-sm-3 col-xs-4 col-md-3 containerbackcards-<%=hiragana.bigletter.downcase.last%>">
<div class="backcard-hiragana">
<h1><b><%= hiragana.ideo1 %></b></h1>
</div>
<div class="card-bigletter">
<h4><%= hiragana.bigletter.upcase %></h4>
</div>
</div>
</div>
</div>
</li>
</ul>
</div>
Thank you for your answer
which ones do you want? what's the logic for which you'd like to choose? If you simply wanted the first 3 you could say
#hiriganas = Hirigana.first(3)
if you have a condition you'd like to choose them by select is a good choice.
#hiriganas = #hiriganas.select{ |hir| # criteria for selection }
It depends: If you have the color saved in the hiragana model, then you just need to:
greenhiriganas = Hirigana.where(color: "green")
If you just know in your head, which letters have which colors, then for each section you need to write an array containing all the names
#if for example A, Ka and Shi are green
green = [:a, :ka, :shi]
#then select via include?-method
greenhiriganas = Hirigana.select{ |hiri| green.include?(hiri.name) }