Using image_tag with srcset attribute? - ruby-on-rails

I try to use an srcset attribute inside an image_tag but I can not make it work.
Im not sure if it is a syntax error or it generally does not work in an image_tag.
Is it possible to use a srcset attribute in an image_tag?
If yes, how?, and if not why not and is there a workaround?
<%= link_to(image_tag("logo.png", alt: "logo", :id => "logo"), root_path) %>

Instead of adding the image_tag to the link_to "name" option you can use open up block and pass your image there.
If you want to use a srcset attribute you could extend the functionality of image_tag by creating a helper:
def image_set_tag(source, srcset = {}, options = {})
srcset = srcset.map { |src, size| "#{path_to_image(src)} #{size}" }.join(', ')
image_tag(source, options.merge(srcset: srcset))
end
It joins each size by comma, so then you can do:
<%= link_to root_path do %>
<%= image_set_tag 'logo.jpg', {
'logo_640.jpg' => '640w',
'logo_1024.jpg' => '1024w',
'logo_1980.jpg' => '1980w'
}, sizes: '100vw', alt: 'logo', id: 'logo' %>
<% end %>
As you can see, the changes introduced in ActionView::Helpers::AssetTagHelper#image_tag in the 5.2.1 Rails version allows you to pass the srcset option, with a hash or an array of 2D arrays containing the different responsive versions of your image:
image_tag("icon.png", srcset: { "icon_2x.png" => "2x", "icon_4x.png" => "4x" })
# => <img src="/assets/icon.png" srcset="/assets/icon_2x.png 2x, /assets/icon_4x.png 4x">
image_tag("pic.jpg", srcset: [["pic_1024.jpg", "1024w"], ["pic_1980.jpg", "1980w"]], sizes: "100vw")
# => <img src="/assets/pic.jpg" srcset="/assets/pic_1024.jpg 1024w, /assets/pic_1980.jpg 1980w" sizes="100vw">
So, if you're using a more recent Rails version, you can just use image_tag instead of writing your own implementation.

Related

How to render an image from a remote url

I want to render a remote url (which is an image) as an image in an active admin show page, but those images are from a collection:
panel 'Media Items' do
render collection: record.chore.media_items.where(discrete_type: :image)
end
But I haven't been able to find anything that does both of these things.
The image url is stored as: chore.media_items.where(discrete_type: :image).first.url for example.
Use literal arbre syntax this way and it might work:
panel 'Media Items' do
record.chore.media_items.where(discrete_type: :image).each do |image|
img(:src => image.url, :style => 'height: 200px')
end
end
Instead of inline-styling, you could also pass a :class => 'yourCustomCssClass' to arbre's img dsl statement. Good luck!
Something like this maybe?
panel 'Media Items' do
record.chore.media_items.where(discrete_type: :image).each do |image|
image_tag image.url
end
end

Ruby hash map with key that contains '-'

How can I add hash map element with a key that contains "-"?
Like this:
<%= button_to_function 'Cancel','cancelRemove("cancelEmail")', :data-dismiss=>'modal', :class=>'btn' %>
I get an error:
undefined local variable or method 'dismiss' for #<ActionView::Base:0x3482fed>
While :'data-dismiss' works, with data attributes you can also do
:data => { :dismiss => 'modal' }
Additional data-prefixed html attributes can be included in the same hash. So for example on another link you might do:
:data => { :remote => true, :method => 'delete' }
which would add to the link the html attributes data-remote="true" data-method="delete".
While the hash syntax is less compact for a single attribute, it's nice when you've got more than one html5 data attribute. And it's arguably a bit more rails-ish.
Just rename it to:
<%= button_to_function 'Cancel','cancelRemove("cancelEmail")', :'data-dismiss'=>'modal', :class=>'btn' %>

Rails: Setting class and data-tag of an HTML attribute with a single rails method

I'm currently working on a tour interface that guides new users around my site. I have a Tour model that has many TourStops, each of which contains information about a section of the site.
Basically, I'd like to write a function for the Tour model that -- when passed the number of a TourStop -- generates the correct class and data attribute for the HTML element it's attatched to. For example, I'd like
<%= link_to image_tag("new_button.png", tour.stop_data(1), :title => 'Add new asset'), new_asset_path %>
to call a function and return something like
def stop_data(order)
" :class => '#{tour_stops.find_by_order(order).name}',
:data => '{:order => order}'"
end
creating a link_to tag like:
<%= link_to image_tag("new_button.png", :class => 'tour_stop_1',
:data => {:order => 1}, :title => 'Add new asset'), new_asset_path %>
The above code doesn't work. Is something like this even possible? If not, what's a better approach I might take?
The image_tag accepts two parameters. A source, and a options Hash.
What you are trying to do is squeezing your return value from stop_data into this options Hash.
In order to get this to work, you first, need to return a Hash from stop_data, and second, make sure you pass only two arguments to image_tag - the source, and the options.
First:
def stop_data(order)
{
:class => tour_stops.find_by_order(order).name,
:data => { :order => order } # you may need order.to_json
}
end
Second:
link_to image_tag("new_button.png", tour.stop_data(1), :title => "Add new asset"), new_asset_path
This looks like it will work, but it won't, since your'e passing three parameters to image_tag.
When you do the following:
image_tag("new_button.png", :class => "tour_stop_1", :data => { :order => 1 }, :title => "Add new asset")
It looks like you're passing even 4 parameters to image_tag, but in fact they are only two. In Ruby, when the last parameter of a method is a Hash, you don't need to wrap the Hash key/value pairs in curly braces ({}), so the example above is essentially the same as
image_tag("new_button.png", { :class => "tour_stop_1", :data => { :order => 1 }, :title => "Add new asset" })
Now, to get your helper to work with image_tag, you need to merge the options, so they become only one Hash.
link_to image_tag("new_button.png", tour.stop_data(1).merge(:title => "Add new asset")), new_asset_path
Again, we're omitting the curly braces when calling merge, because it's only (and therefore last) parameter is a Hash. The outcome is the same as:
tour.stop_data(1).merge({ :title => "Add new asset" })

How to “dynamically add options” to 'form_for'?

I am using Ruby on Rails 3.2.2. In order to implement a "dynamic generated" AJAX style file upload form I would like to "dynamically add options" to the FormHelper#form_for statement if some conditions are meet. That is, at this time I am using code as-like the following (note that I am using the merge method in order to add options to the form_for method):
<%
if #article.is_true? && (#article.is_black? || && #article.is_new?)
form_options = {:multipart => true, :target => "from_target_name"}
else
form_options = {}
end
%>
<%= form_for(#article, :remote => true, :html => {:id => "form_css_id"}.merge(form_options)) do |form| %>
...
<% end %>
However, I think that the above code is too much hijacked.
Is there a better way to accomplish what I am making? For example, can I access from view templates some (unknown to me) instance variable named as-like #form and "work" on that so to change related options as well as I would like? Or, should I state a helper method somewhere? How do you advice to proceed?
BTW: Since the upload process is handled by using a HTML iframe, I am using the remotipart gem in order to implement the AJAX style file upload form - I don't know if this information could help someone...
This looks like a good candidate for a helper method. In your view:
<%= form_for(#article, :remote => true, :html => article_form_options(#article, :id => "form_css_id")) do |form| %>
...
<% end %>
In app/helpers/articles_helper.rb
module ArticlesHelper
def article_form_options(article, defaults = {})
extras = if article.is_true? && (article.is_black? || article.is_new?)
{ :multipart => true, :target => 'form_target_name' }
else
{}
end
defaults.merge(extras)
end
end
Helpers are a good place to keep logic that's too complex for a view but still related to the view.

Rails Helpers with iterators

I have the following helper method:
def tile_for( photo, tileClass = nil )
div_for( photo, :class => tileClass ) do
link_to( image_tag( photo.image_url(:sixth) ), photo )
content_tag( :div, photo.location(:min), :class => 'caption' )
end
end
The problem is, it returns this kind of output:
<div id="photo_25" class="photo">
<div class="caption" style="display: none;">Berlin</div>
</div>
As you can see the link_to is not being output. I guess this is because only the returned value of the block is being included, rather than each executed line? I don't really understand why this kind of code works perfectly in views but doesn't work the same at all in helper methods. Can anyone enlighten me on what's going on and why it works the way it works? How would you build an loop helper method like this?
It works in views since ERB is really just a big concatenation engine. You need to "manually" do this work in your helper, since the code will not be interpreted by ERB:
def tile_for( photo, tileClass = nil )
div_for( photo, :class => tileClass ) do
  link_to(image_tag( photo.image_url(:sixth)), photo)
+ # <- Add this so the block returns the whole string
    content_tag(:div, photo.location(:min), :class => 'caption')
  end
end
div_for also supports arrays, which will be collected into one continuous string. So you can also do like so:
[link_to(image_tag(photo.image_url(:sixth)), photo),
content_tag(:div, photo.location(:min), :class => 'caption')]
Any of the other answers would do. Now, an explanation on why it does works in ERB.
In an ERB template, when a <%= is found, it gets translated to "#{#insert_cmd}((#{content}).to_s)". A simple example: <%= "a" %> gets translated to print "a".
You can take a look at line 631 in erb.rb but skim the previous code for a shake of context (related to the content).
Only the last value of a block is returned. You'll need to make sure your block returns a single value:
def tile_for( photo, tileClass = nil )
div_for( photo, :class => tileClass ) do
link_to( image_tag( photo.image_url(:sixth) ), photo ) +
content_tag( :div, photo.location(:min), :class => 'caption' )
end
end

Resources