How to provide defaults value for partial parameters in Rails? - ruby-on-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.

Related

How does Rails render take options?

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.

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.

Is it possible to render a view with "get parameters"?

Is it possible to render a view with get parameters ?
For example, something like :
render "/projects/sheets?id=43"
It's because I need to render a view which uses url parameters to work properly.
I tried many ways, but it only creates parameters that I can get only in the controller and that are not available after.
It's because I want to have a view that contains the html code of many other views.
This is my current code :
allProjects.html.erb :
<% Project.where(productchief: user.id).order(:title).each do |project| %>
<%= render "/projects/sheets?id=#{project.id}" #This doesn't work. %>
<% end %>
It's because I want to have a view that contains the content of all the other views in my website to allow the users to print all this content in one time.
I don't think it's possible to do it in the way you are trying to do it. You will need to change /projects/sheets to be a partial and render that instead and pass through local variables.
So to clarify /projects/sheets.html.erb becomes /projects/_sheets.html.erb and you would then invoke as:
<%= render partial: "/projects/sheets", locals: { :project_id = project.id } %>
Then within the partial _sheets.html.erb you can make reference to project_id
Generally you should be able to access the params in a view but unless there's a very specific reason you can't, I suggested altering your routes. I may be missing some info from your original question, but let's say you have routes as:
get 'projects/sheets', to: "projects#index", as: :projects
get 'projects/sheets/:id', to: "projects#show", as: :project
That would change your urls a little but would still leave the params available. Using the routes above, for example and going to: localhost:3000/projects/sheets/5?something_fun=geeks_are_us will give the following params:
{"something_fun"=>"geeks_are_us", "controller"=>"projects", "action"=>"show", "id"=>"5"}
And if you're looking to render multiple items via a partial, you can pass the assigned variable for (in your case) 'project' to render the views but it is a partial as you appear to be rendering from a view already.
So something like:
<% #projects.each do |project| %>
<%= render partial: project, locals: {project: project} %>
<% end %>
This would try to render projects/_project.html.erb and sending project as a variable. If you need other variables in your view, just pass them in the locals hash.
Hope this is some help.

form_for Model.new "f.error_messages"

the f.error_messages form builder helper has been depreciated in Rails 3.
How do I get the error messages for a form with the opening tag:
<%= form_for Model.new %>
(I'm using Model.new because I want to be able to load an undefined multiple number of these forms onto a single page)
You can't. Not with a form builder like this one.
Basically, error messages are stored inside the object you use to build a form. If you build a new one every time, you get a clean object without errors.
What you need to do is persist the object filled in by the user between requests. Typically this is done by creating a new object in controller:
#model = Model.new
The essence of this is, that new view uses #model to render a form. And the trick is to have a possibility to render the same view in other actions that also provide #model. That said, if you do something like this in create:
#model = Model.new(model_params)
if #model.save
# success
else
render :new
end
It can render new view, because it assigns #model too; in this case, it will contain errors with messages and other stuff. All this is inside #model.errors – which is always empty in new action.
It's not that different for multiple forms, bear in mind that you always submit only one. You may switch to rendering an array of forms, in that case you could have #models array:
#models = [form1, form2, form3]
In that case, if saving fails, assign that array again and either replace the form the user tried to fill in (if you can identify it), ot prepend/append that form with errors to that array.
#models[index_of_submitted_form] = form_from_user #replace
#models << form_from_user # append
#models.unshift form_from_user # prepend
In Rails 3, you can access errors using #model.errors
<%= form_for #model = Model.new do |f| %>
<% #model.errors.full_messages.each do |msg| %>
<p><%= msg %></p>
<% end %>
# ...
# ...
# ...
<% end %>

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