How does Rails render take options? - ruby-on-rails

I'm going thru the guide and stuck with the below example:
What is the search: #q part about? I can see it as a hash option that is passed to the partial, but how is that used? The partial uses #q but what does that have to do with the key search? And the yield f? Does that just put the markup <p>
Title contains: <%= f.text_field :title_contains %>
</p> onto the screen?

In both example search: #q is passed as locals into the partial. Which is eventually not getting used anywhere in 'search_filters' partial.
For more details see http://api.rubyonrails.org/classes/ActionView/Helpers/RenderingHelper.html#method-i-render
Specially this line.
If no options hash is passed or :update specified, the default is to
render a partial and use the second parameter as the locals hash.

Related

How to provide defaults value for partial parameters in Rails?

I need to render a shared partial which can receive a few parameters from several views, but I don't want to pass all parameters everytime. If I call the template without all parameters, I get an error.
Is there a way to define default values for parameters, only if they haven't been defined when calling render 'name_of_partial?
This should do the trick:
<% my_param ||= 'default value' %>
A partial that contains this can be rendered with or without providing my_param.
After reading the docs, and some head scratching, I was able to define default values for parameters not passed to the template.
# in views/shared/template.html.erb
<% my_param = 'default_value' unless binding.local_variable_defined?(:my_param) %>
# Now you can call the partial with or without setting `my_param`
# Now you can call the partial without parameters...
<%= render 'shared/my_template' %>
# ...or with parameters
<%= render 'shared/my_template', my_param: 'non-default value' %>
Tested with Ruby 2.3.1 and upwards.

Rails local_assign vs. local variables

Learning from the Rails guide, I don't understand how local_assign works below:
To pass a local variable to a partial in only specific cases use the
local_assigns.
index.html.erb
<%= render user.articles %>
show.html.erb
<%= render article, full: true %>
_articles.html.erb
<h2><%= article.title %></h2>
<% if local_assigns[:full] %>
<%= simple_format article.body %>
<% else %>
<%= truncate article.body %>
<% end %>
This way it is possible to use the partial without the need to declare
all local variables.
How is the partial even being rendered by the show action when it has the name _articles, which will only show for the index action? I also don't see why you use add the option of full: true when you could have just used locals: {full:true}. What's the difference?
Regarding the use of local_assigns:
The point of this section of the guide is to show how to access optional locals in your partials. If a local variable name full may or may not be defined inside your partial, simply accessing full will cause an error when the local is not defined.
You have two options with optional locals:
First, using local_assigns[:variable_name], which will be nil when the named local wasn't provided, or the value of the variable.
Second, you can use defined?(variable_name) which will be nil when the variable is not defined, or truthy (the string "local_variable") when the local is defined.
Using defined? is simply a guard against accessing an undefined variable, you will still have to actually access the variable to get its value:
if local_assigns[:full]
if defined?(full) && full
As for your specific questions:
How is the partial even being rendered by the show action when it has the name _articles, which will only show for the index action?
This is a typo. The correct partial name is _article.html.erb. Regardless of the action, index or show, the correct partial name is the singular of the model. In the case of rendering a collection of models (as in index.html.erb), the partial should still be singularly named.
I also don't see why you use add the option of full: true when you could have just used locals: {full:true}. What's the difference?
The point is that the full: true syntax is shorter. You have two identical options:
render partial: #article, locals: { full: true }
or
render #article, full: true.
The second is significantly shorter and less redundant.

Difference between render 'posts' and render posts (without quotes)

I was going through a video and where he was rendering something like below example:
<div>
<%= render posts %> # no quotes to posts
</div>
Though he has even created a partial with _posts.html.erb, he is calling with quotes to posts.
Though he has mentioned something about it like it calls, active record model, then class and then something...i could not understand it properly. Can anyone explain clearly this with simple example.
Render with quotes
<%=render 'post'%>
Rails is going to look in the current folder for a partial file which starts with _
Render without quotes
Is still going to use the same partial, but post in this case is a variable. I think this is translating to this:
<%= render partial: "post", locals: {any_string: your_variable(in this case is post)} %>
Again I haven't checked that.
The _post.html.erb is the partial, which can look like this:
<b><%=any_string%></b>
If your_variable which was assigned to any_string will contain the string 'My name is'
Your partial will print 'My name is' in bold.
Anyway partial are more complex, and they are used for DRY-ing (Don't repeat yourself) the code.
You can see more examples here.
With quotes then you are explicitly rendering a partial of that name. Without quotes something quite interesting is happening. posts (without quotes) is a variable that will be an activemodel list of records.
Now what the call to render does is it will look at the type of each of the models and then find the correct partial for the model (which will be the name of the model camel_cased) and render each one in turn.
EDIT:
If you have a model called Post and you assign some of those records to a variable (he uses posts I assume but I'll use foo to disambiguate) like so:
foo = Post.all
then by calling render foo the render function will see that you have an activerecord collection of records, it will then check the model associated with these records (Post in our example) and will loop through all of them rendering them to a partial called _post.html.erb with a local variable for each record assigning the record to post.
<%= render foo %>
is equivalent to:
<% foo.each do |my_post| %>
<%= render partial: "post", locals: {post: my_post} %>
<% end %>

How to render a collection as succinctly with Haml as with ERB?

In an ERB view, you can call
<%= render #cart.line_items %>
and, assuming you have a partial named _line_item.html.erb in the line_items directory, Rails will take care of the rest.
But I'm having some trouble converting this to something similarly succinct in Haml.
I can get something that works by explicitly calling .each on #cart.line_items or an equivalent local variable in the partial, but calling .each is exactly what I'm trying to avoid.
Here's the partial:
%tr
%td= item.product.title
%td= item.quantity
%td.item_price= number_to_currency(item.total_price)
From what I've read so far, this looks like it should produce the same behavior:
= render 'line_items/line_item', collection: #cart.line_items, as: :item
But it's still not working as expected. item isn't being passed as a local:
undefined local variable or method `item' for #<#<Class:0x007fb3c531aee0>:0x007fb3c3692160>
Does Haml have a comparably succinct way to render collections (relative to ERB)? If so, any thoughts as to what I might be missing? Or if there's a better way to do this altogether?
Thanks.
Try this(mention partial explicitly)
= render :partial => 'line_items/line_item', collection: #cart.line_items, as: :item
The render method and how it handles partials is part of Rails, and should work the same with Erb and Haml.
When using render to automatically render a collection, as in this case with render #cart.line_items, Rails will use the name of the class each entry of the collection to determine both the partial to use and the name of the local variable used in that partial.
In your Erb example Rails is using the partial _line_item.html.erb, which suggests that the objects are of type LineItem and so Rails will create a local named line_item. However in your Haml partial you are using the name item, which isn’t being defined hence the error.
Simply change all occurances of item to line_item in your partial, and you will be able to use
= render #cart.line_items
in your Haml, the same as in Erb.

NoMethodError in Rails: Passing variables

I'm trying to pass in the variable "post" to a partial. The partial is being used on both my show#view & I'm also rendering a collection using it. Here's what it looks like (notice the "#"):
##Show#View
<%= render 'my_partial/my_view', post: #post %>
##Collection ## (I'm not using the "#" symbol)
<%= render 'my_partial/my_view', post: post %>
#Mypartial
<% if #post.something? %>
## do this
<% else %>
## do that
<% end %>
And then I get the beautiful NoMethodError undefined methodsomething?' for nil:NilClass` page when using it in my collection. I know why I'm getting it, I'm just wondering what's the DRY way(s) of getting this to work? Should I just create another partial?
Thank you
Gave my solution below.. Though, it's probably not the best way...
Why are you referencing #post in your partial? You should use post instead, that is the entire point of what you are doing (passing variables to a partial as local variables).
You must change the rendering to:
<%= render 'my_partial/my_view', locals: { post: #post } %>
Take a look in the rails guide: http://guides.rubyonrails.org/layouts_and_rendering.html
Search for "3.4.4 Passing Local Variables" and you will find more information about this whole stuff.
And for fixing your collection problem also check out the rails guide and search for: "3.4.5 Rendering Collections".

Resources