I'm just starting with rails.
I'm trying to render partial view in my controller's method, but I'm gettin with brackets.
My appplication_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def inline_error(field, messages_bag)
if msg = messages_bag.errors.full_messages_for(field).first
msg = '<small class="error">'+msg+'</small>'
return msg.html_safe
end
end
helper_method :inline_error
#nevermind on param
def take_flash(type)
render "class/flash", :layout => false
end
helper_method :take_flash
end
my class/_flash.html.rb file:
hello
and I'm gettin
["hello"]
Could anybody help me?
In fact there are two different render methods, one is defined on ActionView, and the other one is defined on ActionController - they slightly differ in what they do. What you need here is render of the view:
def take_flash(type)
view_context.render "class/flash"
end
I think you're confusing the usage of helpers and partials. They are both used to generate segments of view code but you don't need to use both at once.
In your case you can either pass your param to the partial directly, and remove the helper.
Your view:
<%= render 'flash', locals: {my_param: 'blahblah'} %>
Your _flash partial:
Hello <%= blahblah %>
Or you can pass it to the helper and remove the partial.
Your view:
<%= take_flash('blahblah') %>
Your helper method (define this in a helper file, not your controller):
def take_flash(my_param)
"Hello #{blahblah}"
end
Both approaches have the same outcome.
Related
In my layouts/books I have my navigation code that is hitting all my views for books then yields.
I also have my header code that is hitting all my views for books then yields. Really I only want my header code to hit the index and show but not my order.
I've tried putting something like below around my header to:
<% if #render.header == true %>
<div class="header">
HTML
</div>
<% end %>
Then putting in my views:
<% if #render.header == true/false %>
I end up getting undefined method `header'. I've also tried things like:
What do I need to adapt this into to get this to work? Should I be putting something into my controller?
EDIT: I've also attempted the following in both my layout/books and in index:
<% #render.header = true %>
This leads to undefined meathod "header= "
I can get this to work using a global variable but that has got to be a bad idea.
You can do this:
if #render && #render.header
So after flailing around I went from in my layout/book:
<% if #render.header == true/false %>
To:
<% $header == true/false %>
Which worked but it's global. And finally to updating my controller for both index and show to:
def index
#header = true
end
And then my layout/book to:
<% #header == true %>
So yeah. Variable types.
This is nothing terribly different from what I already pointed out in the comments but here it goes:
First of all, #render just defaults to nil in views. So when you are trying to set the value for header, it fails with undefined method 'header=' for nil:NilClass (because Rails looks for a method called #header= for the nil:NilClass).
Use a plain variable #render_header
You can instead just set a plain variable, such as #render_header, in your controller or view (before rendering header):
<% #render_header = true %>
and then in your header
<% if #render_header %>
# ...
Use a hash for #render = { header: true }
Or, if you really want to use the #render variable, you can just set it to be a hash:
<% #render = { header: true } %>
and then in your header
<% if #render.present? && #render[:header] %>
# ...
This is almost like what you originally wrote.
Set the variable automatically based on controller action
If you need this variable to be set for multiple controller actions, you can look into controller filters. They work just like callbacks but for controllers.
For example, you could do:
class MyController < ApplicationController
before_action :set_render_header_to_true, only: [:new, :show]
# ...
private
def set_render_header_to_true
#render_header = true
end
end
That way, the variable would automatically be set to true for the :new and the :show action. If you had multiple controllers needing to set render_header to true, you could move the set_render_header_to_true method to the ApplicationController. For example, this would work:
class ApplicationController
private
def set_render_header_to_true
#render_header = true
end
end
class MyController < ApplicationController
before_action :set_render_header_to_true, only: [:new, :show]
# ...
end
class MyOtherController < ApplicationController
before_action :set_render_header_to_true, except: :index
# ...
end
Set a custom layout from your controller
You can also specify a specific layout to be rendered based on the controller action:
class MyController < ApplicationController
layout 'with_header'
# Use the line below to render header only for specific actions,
# such as new and edit
# layout 'with_header', :only => [:show, :edit]
# ...
end
You can even define a method to dynamically choose the layout:
class MyController < ApplicationController
layout :determine_layout
def determine_layout
if action_name == :new
'layout1'
elsif action_name == :show
'layout2'
else
'application'
end
end
# ...
end
I don't think i understand your problem. Anyway you can also use content_for. For example, if add to layout/books.html.erb:
<%= yield :header %>
For index/show/etc views with header:
<% content_for :header do %>
<div class="header">
HTML
</div>
<% end %>
Let's say I have an <h1> or <p> element in the HTML file of my Rails project.
This element is used to produce the name of the page. To follow DRY, I want to make this text dynamic by passing the parameters (which correspond to the page's name) to the <h1> instead of creating many static pages.
Since I am passing the parameters in the application_controller.rb, I am doing it by setting a category:
def set_category
session[:category] = params[:category] if params[:category].present?
end
Now, what I want to do, if the page becomes /history, is to send the history category into the <h1>. From reading the documentation, a content_tag seems to be the best way to do this, so I tried:
<%= content_tag :div, data: {session[:category]} do %>
No luck there. Any ideas on how to use a parameter as text?
I'm not sure if it's the best way to implement this. I believe the rails way is to use a helper and Content_For
But for the sake of answering your question here is how your approach would work.
I think you may have forgotten the before_action filter.
application_controller
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_action 'set_category'
def set_category
session[:category] = params[:category] if params[:category].present?
end
end
The step of setting content of an <h1> is like this
view.erb
<%= content_tag :h1, session[:category] %>
OR
view.erb
<h1>
<%= session[:category] %>
</h1>
Every time I try to render something that is located from a different view, I get a NoMethodError: undefined method `each' for nil:NilClass.
It happens when I put the following code in the view I want to render stuff on:
views/uploads/myuploads.html.erb
<%= render template: 'guitar_sounds/index' %>
And it tells me that the error seems to be in a particular block of code where the template is located:
views/guitar_sounds/index.html.erb
<% #guitar_sounds.each do |sound| %> <!-- Error here -->
<%= render "guitar_sound", sound:sound %>
<% end %>
However, when I load that page view on its own, I get no errors.
Can someone help me?
Loading a partial does not automatically hit a controller method. By that, it sounds like the only controller method being run is uploads#myuploads, but your #guitar_sounds variable is being defined in guitar_sounds#index. I'd simply define the #guitar_sounds variable in your UploadsController
UploadsController < ApplicationController
def myuploads
# here is where #guitar_sounds needs to be defined
#guitar_sounds = GuitarSound.all
end
end
Let's say you needed #guitar_sounds in lots of methods, you could define it in a before_action
UploadsController < ApplicationController
before_action :set_guitar_sounds
def myuploads
# normal controller code
end
private
def set_guitar_sounds
#guitar_sounds = GuitarSound.all
end
end
Now #guitar_sounds will be set for every method in the UploadsController
Your template guitar_sounds/index expects #guitar_sounds to be defined, and be able to iterate over its items.
If you reuse the template without assigning any values to #guitar_sounds, by default it will be nil, thus you can see the error.
Hope it clarifies a bit the problem!
guitar_sounds/index expects #guitar_sounds to be defined, aka, not nil, so it can iterate over its items.
You should, instead, use local variables.
<%= render template: 'guitar_sounds/index', guitar_sounds: #guitar_sounds %> #or other # variable
And at your view:
<% guitar_sounds.each do |sound| %>
<%= render "guitar_sound", sound:sound %>
<% end %>
Now guitar_sounds (note the missing #) is a local variable that you pass to the render function!
EDIT: Check rails documentation about this: Passing Local Variables to partials/templates.
In my Rails app I already have the following code:
<% %w(number_of_students edit_class_name tech_help).each do |modal| %>
<%= render "common/modals/#{modal}" %>
<% end %>
There will be a few more modals added into app/views/common/modals and instead of explicitly listing them out in the %w() I was wanting to loop through the common/modals directory and just render each file.
Here is what I came up with:
def render_modals
files = Dir.glob("#{Rails.root}/app/views/common/modals/*").collect { |file| File.basename(file, ".html.erb").sub("_", "") }.flatten
files.collect do |modal|
render partial: "common/modals/#{modal}"
end.join.html_safe
end
define a simple method in where is more appropriate (maybe app helper?) like this:
def modals
%w(number_of_students edit_class_name tech_help)
end
if you need these modals in a controller/model too, maybe you should define this method in an appropriate class? For example
class Modal
def self.types
%w(number_of_students edit_class_name tech_help)
end
end
Also, if you are rendering the templates often, then also define
def render_modals
modals.map do |modal| # Modals here should be the method that you just defined, example, Modal.types
render partial: "common/modals/#{modal}"
end.join
end
Ok so I have a helper method in the application controller:
def run_test(test_name)
#computation stuff
render :partial => test_name
end
And I call it like so in views:
<%= run_test("testpartial") %>
and it renders ok with only 1 (although... the render partial seems to be returning an array instead of just the partial content?), but if I put the run_test helper call in the view twice I get a double render error, which shouldn't be happening with partials.
Any ideas?
render in a controller versus render in a view are different methods. The controller eventually calls render on a view, but the controller's render method itself expects to be called only once. It looks like this:
# Check for double render errors and set the content_type after rendering.
def render(*args) #:nodoc:
raise ::AbstractController::DoubleRenderError if response_body
super
self.content_type ||= Mime[formats.first].to_s
response_body
end
Note how it raises if called more than once?
When you call helper_method you give the view a proxy to the controller's version of render, which is not intended to be used in the same way as ActionView's, which is, unlike the controller's, expected to be called repeated to render partials and whatnot.
Looks like in Rails 3.2 this just works:
# application_helper.rb
def render_my_partial
render "my_partial"
end
You could try using render_to_string method in the view helper
render_to_string :partial => test_name, :layout => false