If working in Rails, what's the best way to define a helper function that generates many image tags? This function would then be called from a .erb file, producing a view.
In other words, something like
def build_view; image_tag("seg-433.png", :alt => "Shit don't work", :class => "round"); end
but that returns many tags.
Feel free to suggest a more idiomatic approach, I just started riding the Rails train, like, yesterday.
If you have an image model you could create a helper like this:
/app/helpers/my_controller_helper.rb
module MyControllerHelper
def bunch_of_image_tags
images = []
Image.all.each do |image|
images << image_tag(image.path, :alt => image.alt, :class => image.class)
end
images.join("<br/>")
end
end
You could also get a list of files from the file system, but I'm not sure what you would use for the alt tag in that case. Also look at paper_clip - https://github.com/thoughtbot/paperclip
You could render a collection of partials which contains everything you need for your image tag.
render :partial => "image", :collection => #images
the partial "image" being the one containing the image tag.
More at api.rubyonrails.org.
Related
I've been trying to render a underscore.js template, that is just like ERB on some haml content.
But as the templates grow I dont want to do more like this
%script{:type => "text/template", :id => "my_template"}
:plain
<div><%= my_js_value %></div>
.....(other content)...
and instead of that I want to use partials to render inside it I wish to do something like:
%script{:type => "text/template", :id => "my_template"}
=render :file => "mytemplate.txt"
But that rendering tried to bind the ERB on it, and I've got errors for my_js_value
The only way I get to do this rendering is through that way:
%script{:type => "text/template", :id => "my_template"}
=render :text => File.read("#{Rails.root}/app/views/mycontroller/mytemplate.txt")
The last one worked for me, but I was looking for something better than that.
What do you suggest me to do instead of reading a file? Isn't there a "raw" option for render?
BTW this is on a rails 2.3.14 app
I don't know HAML to well yet but there is a raw option for rails. You can find the details at the link below:
<%= raw #user.name %>
http://api.rubyonrails.org/classes/ActionView/Helpers/OutputSafetyHelper.html
I have created a custom helper in my application.rb file, which looks like:
module ApplicationHelper
def add_feature_field(feature_type, object_form_builder, actions_visible)
object_form_builder.object.features.build
fields = object_form_builder.fields_for :features do |features_builder|
render :partial => "features/fixed_feature", :locals => {:feature => features_builder, :fixed_feature_type => feature_type, :form_actions_visible => actions_visible}
end
end
end
I am calling this helper from my view like so:
<%= add_feature_field("First Name", customer, false) %>
<%= add_feature_field("Last Name", customer, false) %>
<%= add_feature_field("Date of Birth", customer, false) %>
This is working pretty much as anticipated, except for one major hurdle: the second time the helper is called, it renders 2 fields instead of a single field, and the third time it renders 3 fields.
I assume that what is happening is that the fields_for loop in my helper is picking up the previously built objects, and also rendering those - can anyone suggest a way of preventing this?
EDIT: For clarity, as per the comments, this helper method is being used within the Customer form; the Features being created are nested attributes.
I've made a small gist of it but basically what I'd like to do is
<%= render "this is a #{ I18n.translate(:a_string) }" %>
Saving snippets and/or entire views_templates (like views/mailers/some_model/regards.haml) in a database will allow me to building hierakies of templated views entirely from the web-interface - and saving the designers the round-trip to uploading files to the server, or in less pompous circumstances, having users edit minor screw-ups in templates themselves.
The above example does in no way portray the size of the problem - but point to the heart of it: how to render a template usually sitting in the filesystem, now being a text attribute on a Template model in the database.
You could render a partial for a generic template:
<%= render :partial => 'shared/string' %>
Then the partial template does the string rendering:
In shared/_string:
<%= "this is a #{ I18n.translate(:a_string) }" %>
Or you could have the database lookup for stored string done in the render call, and pass it in as a parameter:
<%= render :partial => 'shared/string', :locals => {:a_string => String.find(:string_id)} %>
In shared/_string:
<%= a_string %>
Or if your string contains ERB that needs to be executed try:
<%= ERB.new(a_string).result %>
Rendering a partial like this shouldn't cause the DoubleRender error.
The answer - at least to my question - is rather convoluted, and requires a great deal of digging into the innards of the Rails stack! I'm merely copy-cat'ing here: go see the complete answer in José Valím's book, Crafting Rails Applications!
With Rails 3.2.1 (possibly even before) templates (as in eg. app/views/posts/show.haml) are 'found' by something called a Resolver, and it is possible to add ones own Resolver - which I did :)
I added a
class ViewTemplate < ActiveRecord::Base
class Resolver < ActionView::Resolver
def find_templates(name, prefix, partial, details)
conditions = {
:path => normalize_path(name, prefix),
:locale => normalize_array(details[:locale]).first,
:display_format => normalize_array(details[:formats]).first,
:handler => normalize_array(details[:handlers]),
:partial => partial || false
}
ViewTemplate.where(conditions).map do |record|
initialize_template(record)
end
end
end
end
and then I told my ApplicationController to look at my own path first:
class ApplicationController < ActionController::Base
append_view_path ViewTemplate::Resolver.new
end
and finally I added a record to my ViewTemplate with
ViewTemplate.create( content: '=render "form"', path: 'posts/edit', display_format: 'html', handler: 'haml' )
and replaced the contents of my views/layouts/application.haml with:
= render 'posts/edit'
and huzzah :)
(well, more or less - there are of cause issues like variables, and scopes - but hey, nothing is perfect)
I am using Ruby on Rails 3.1 and I would like to know how to correctly handle internationalization related to partial template files. That is, ...
... in my app/views/users/flag.html.erb file I have:
<%= t('.test_key1') %>
<%= render :partial => "/users/flag_form" %>
... in my app/views/users/_flag_form.html.erb file I have:
<%= t('.test_key2') %>
If in my config/locales/views/users/en.yml file (note: I am organizing files as stated in the official RoR guide) I use
en:
users:
flag:
test_key1: Test 1 text
test_key2: Test 2 text
the Test 1 text is displayed in the "main" template (app/views/users/flag.html.erb) but the Test 2 text isn't for the partial template (app/views/users/_flag_form.html.erb). How could\should I solve this issue so to properly display the Test 2 text?
config/locales/en.yml
en:
users:
flag:
test_key1: Test 1 text
flag_form:
test_key2: Test 2 text
app/views/users/flag.html.erb
<%= t('.test_key1') %>
<%= render :partial => "/users/flag_form" %>
app/views/users/_flag_form.html.erb
<%= t('.test_key2') %>
NB:
Rails path to the view must match YAML path to the symbol. You need to create an entry at YAML file that matches the name of the view. Omit the trailing underscore since it's a partial.
Read more about lazy lookups
One way would be to using scope, instead of "lazy loading" using the full stop.
Something like this should work:
I18n.t :test_key2, :scope => 'users.flag'
or use:
I18n.t "users.flag.test_key2"
Lastly, you could even pass it to the partial as in
<%= render :partial => "/users/flag_form", :locals => { :test_key => t('.test_key1') } %>
You should also checkout the appendix section on this website as it might be listing something that I am missing:
https://web.archive.org/web/20120619002316/http://www.unixgods.org/~tilo/Rails/where_is_Rails_trying_to_lookup_L10N_strings.html
I wrote this. What do you think about it?
def translate_for_partials key, *args
I18n.t("#{params[:controller].gsub('/', '.')}.#{params[:action]}.#{key}", *args)
end
Is that bad to make such a method ?
I'm trying to generate a JSON response that includes some HTML. Thus, I have /app/views/foo/bar.json.erb:
{
someKey: 'some value',
someHTML: "<%= h render(:partial => '/foo/baz') -%>"
}
I want it to render /app/views/foo/_baz.html.erb, but it will only render /app/views/foo/_baz.json.erb. Passing :format => 'html' doesn't help.
Beginning with Rails 3.2.3, when calling render :partial (only works outside of the respond_to block).
render formats: [ :html ]
instead of
render format: 'html'
What's wrong with
render :partial => '/foo/baz.html.erb'
? I just tried this to render an HTML ERB partial from inside an Atom builder template and it worked fine. No messing around with global variables required (yeah, I know they have "#" in front of them, but that's what they are).
Your with_format &block approach is cool though, and has the advantage that you only specify the format, whereas the simple approach specifies the template engine (ERB/builder/etc) as well.
Rails 4 will allow you to pass a formats parameter. So you can do
render(:partial => 'form', :formats => [:html])}
Note you can do something similar in Rails 3 but it wouldn't pass that format to any sub partials (if form calls other partials).
You can have the Rails 4 ability in Rails 3 by creating config/initializers/renderer.rb:
class ActionView::PartialRenderer
private
def setup_with_formats(context, options, block)
formats = Array(options[:formats])
#lookup_context.formats = formats | #lookup_context.formats
setup_without_formats(context, options, block)
end
alias_method_chain :setup, :formats
end
See http://railsguides.net/2012/08/29/rails3-does-not-render-partial-for-specific-format/
For Rails 3, the with_format block works, but it's a little different:
def with_format(format, &block)
old_formats = formats
self.formats = [format]
block.call
self.formats = old_formats
nil
end
Building on roninek's response, I've found the best solution to be the following:
in /app/helpers/application.rb:
def with_format(format, &block)
old_format = #template_format
#template_format = format
result = block.call
#template_format = old_format
return result
end
In /app/views/foo/bar.json:
<% with_format('html') do %>
<%= h render(:partial => '/foo/baz') %>
<% end %>
An alternate solution would be to redefine render to accept a :format parameter.
I couldn't get render :file to work with locals and without some path wonkiness.
In Rails 3, the View has a formats array, which means you can set it to look for [:mobile, :html]. Setting that will default to looking for :mobile templates, but fall back to :html templates. The effects of setting this will cascade down into inner partials.
The best, but still flawed way, that I could find to set this was to put this line at the top of each full mobile template (but not partials).
<% self.formats = [:mobile, :html] %>
The flaw is that you have to add that line to multiple templates. If anyone knows a way to set this once, from application_controller.rb, I'd love to know it. Unfortunately, it doesn't work to add that line to your mobile layout, because the templates are rendered before the layout.
Just elaborating on what zgchurch wrote:
taking exceptions into account
returning the result of the called block
Thought it might be useful.
def with_format(format, &block)
old_formats = formats
begin
self.formats = [format]
return block.call
ensure
self.formats = old_formats
end
end
You have two options:
1) use render :file
render :file => "foo/_baz.json.erb"
2) change template format to html by setting #template_format variable
<% #template_format = "html" %>
<%= h render(:partial => '/foo/baz') %>
I had a file named 'api/item.rabl' and I wanted to render it from an HTML view so I had to use:
render file: 'api/item', formats: [:json]
(file because the file have no underscore in the name, formats and not format (and passes and array))
It seems that passing a formats option will render it properly in newer Rails version, at least 3.2:
{
someKey: 'some value',
someHTML: "<%= h render('baz', formats: :html) -%>"
}
I came across this thread when I was trying to render an XML partial in another xml.builder view file.
Following is a nice way to do it
xml.items :type => "array" do
#items.each do |item|
xml << render(:partial => 'shared/partial.xml.builder', :locals => { :item => item })
end
end
And yeah... Full file name works here as well...