I'm building a header partial for a view. I call the partial as following:
<%= render :partial =>"project/header", :locals => {:right_header => 'BLAH BLAH'} %>
The header has a default right_header, but I'd like the option to overwrite it:
<div id="header">
<span class="right">
Standard Header here
</span>
</div>
The deal is when calling the partial, right_header won't always be defined, I'd like for it to be optional, but that's where I'm struggling and rails keeps erroring... In the partial I've been trying:
<% if right_header.empty? %>
default header....
<% else %>
<%= right_header %>
<% end %>
Suggestions? Am I passing this correctly to the partial with locals?
Thank you
use
if defined? right_header
another way is
right_header ||= 'default'
in the view. so if right_header is not passed in, its value will be default. You can pass in any value too, and later on just do things according to the value of right_header.
Related
Rails beginner project
In a view that I want to show all results from two different models (MediaLink & MediaCollection)
I have successfully created a two dimensional array #ranked_media that holds
[0] is id from table
[2] is total_points
[5] is class name as a string.
Being that they are pulled from 2 different tables, the id can be repeated twice, but won't have the same class name if id are identical.
In my view (pages/media.html.erb) :
<div class="container">
<% #ranked_media.each do |media| %>
<% if media[5] == 'MediaCollection' %>
<%= render 'media_collection_show' %>
<% else %>
<%= render 'media_link_show' %>
<% end %>
<% end %>
</div>
partial _media_collection_show.html.erb :
<h2 class="text-center mt-4 text-white">
is a MEDIA COLLECTION of class :: <%= #media_collections.class %>
</h2>
partial _media_link_show.html.erb :
<h2 class="text-center mt-4 text-white">
is a MEDIA LINK of class :: <%= #media_links.class %>
</h2>
At this point all is fine. The block is being generated 5 times and applies the output according to the each block. The partials also outputs the class name to make sure they all work.
My Problem
However, I can add this to the view (3rd line is the change) :
<div class="container">
<% #ranked_media.each do |media| %>
<%= media %>
<% if media[5] == 'MediaCollection' %>
<%= render 'media_collection_show' %>
<% else %>
<%= render 'media_link_show' %>
<% end %>
<% end %>
</div>
And it outputs media object.
But if try that in any of the partials like this :
<h2 class="text-center mt-4 text-white">
is a MEDIA LINK of class :: <%= #media_links.class %>
<%= media %>
</h2>
I get NameError in Pages#media
undefined local variable or method `media'
How can I access the media block element inside these two partials?
By sending in local variables to the partials: <%= render 'media_link_show', media: media %>. In the partial you'd access it like so:
<h2 class="text-center mt-4 text-white">
is a MEDIA Object of class :: <%= media.class %>
</h2>
Note the lack of #; in general it's bad form to access instance
variables in partials.
Your third line (<%= media %>) also does nothing for the functionality, and can safely be removed.
Edit: When a controller does #items = ... in an action, it sets #items as an instance variable; any view rendered from that action has access to that value, which is why your #ranked_media can be looped over.
Partials are meant to be re-used, and not necessarily by the same view/controller, so if a partial accesses an instance variable, like your _media_collection_show.html.erb does, then any view/controller that uses that partial will need to set the #media_collections variable.
To avoid this (a controller action should strive to only set one instance variable), you can instead inject local variables into a partial: <%= render "partial_name", items: #items %>. Here, we're setting the local variable items to be the value of #items. By doing this, any view that uses the partial can send in their own items into the partial, without the need for the controller to set a "magical variable" #items. You could, for example, do: <%= render "partial_name", items: [1, 2, 3] %>. You've decoupled the partial from the controller.
i have a shared template that i use regularly to show errors like :
<%= render "shared/flash_error", :error => flash[:error], :info => flash[:info] %>
This variables are optional, so my view is like :
<% if defined?(error) and error %>
<div class="error">
<%= error %>
</div>
<% end %>
<% if defined?(info) and info %>
<div class="info">
<%= info %>
</div>
<% end %>
Now, there are cases when i would need to add a new optional variable and extend the partial with other types of errors. This, however, can make the rendering hard to keep up with. I would now need to edit every render and change it to :
<%= render "shared/flash_error", :error => flash[:error], :info => flash[:info], :new_entry => flash[:new_entry] %>
and so on with every new entry. So, i am wondering, is there a way for the rendered partial to 'observe' specific flash variables,so that i don't even have to pass them as attributes to the rendered view ?
Yeap, tried it and works fine. Something like :
<%= render "shared/flash_error", msg_notifications %>
where msg_notifications is just a hash filled with everything, so it can be changed in a DRY way. Simple, yet sometimes the mind plays bad games :P
In one of my views I apply a layout to a block of code:
# In app/views/sessions/new.html.erb
<% render :layout => 'home/shadow_box' do %>
#... code for sign in form here
<% end %>
The layout is a div that has png shadows on all four sides.
Since I use this layout all over my site, I want to pass a variable to the layout that specifies the width of the shadowed div. I tried using content for in the code block:
# In app/views/sessions/new.html.erb
<% render :layout => 'home/shadow_box' do %>
<% content_for :box_width %>640<% end %>
#... code for sign in form here
<% end %>
# In app/views/home/_shadow_box.html.erb
<div class="shadow-one" style="width:<%= yield :box_width %>;">
<div class="corner-a"></div>
<div class="corner-b"></div>
<div class="shadow-two">
<div class="shadow-three">
<div class="shadow-four">
<%= yield %>
</div>
</div>
</div>
</div>
This didn't work and instead resulted in a double render of the entire code block.
What's the best way to tackle this problem?
Figured it out.
From the API: "You can also yield multiple times in one layout and use block arguments to differentiate the sections."
Solution:
# In app/views/sessions/new.html.erb
<% render :layout => 'home/shadow_box' do | section | %>
<%- case section when :box_width -%>
#width goes here. I.e., 640px
<%- when :content -%>
#code block goes here
<% end -%>
<% end %>
#In app/views/home/_shadow_box.html.erb
<div class="shadow-one" style="width:<%= yield :box_width %>;">
<div class="corner-a"></div>
<div class="corner-b"></div>
<div class="shadow-two">
<div class="shadow-three">
<div class="shadow-four">
<%= yield :content %>
</div>
</div>
</div>
</div>
First you need to know the difference between layouts and partials. Partials are generally from the view but can also be used from the controller if you are using ajax. Layouts are almost always used in the controller.
First create a file in a shared folder such as application/ and in this folder put a file call it whatever you want but it will contain the material that you want to include all over your site. Then when you pass a variable to a partial it's called in the partial as a local variable. Also with partials you don't need to say render :partial => you just put render 'application/some_file'
So from the view you want this:
<%= render 'application/your_file', :div_size => '600' %>
And then from the partial in the folder such as application/your_file.html.erb do this:
<div style="width:<%= div_width %>px;">
content
</div>
So here is my form
<% remote_form_for([:admin, Page.new]) do |f| %>
<ol>
<li>
<%= f.label(:title) %>
<%= f.text_field(:title) %>
</li>
<li>
<%= f.label(:parent_page) %>
<%= f.select(:parent_page_id, Page.roots.map { |p| [p.title, p.id] }.unshift(["none", nil])) %>
</li>
</ol>
<div class="modal-controls">
<%= submit_tag("Save") %> or <%= link_to_function("cancel", "R.Pages.hideAdd();") %>
</div>
<% end %>
And my action
def create
#page = Page.create(params[:page])
#languages = Language.all
#languages.each do |language|
#page.page_languages.create(:language_id => language.id)
end
For some reason the submitted for does not call the create.js.rjs template, but instead tries to call create.html.erb, do i need some sort of extra setting with the form?
btw i am using rails 2.3.5
I can't remember the exact default behavior in rails, but have you tried putting a respond_to at the end of your controller action:
respond_to(:html, :js)
Hope this helps.
EDIT
I went back to check on the default behavior for Rails in respect to rendering views. Rails favors convention over configuration in this instance. The default behavior is that Rails automatically renders views with names that correspond to actions. You don't need the respond_to any more if you stick to this convention. Here is the documentation.
Just wanted to update my post with the correct info... glad you figured your problem out.
I had named my template create.rjs.js instead of create.js.rjs, thats why i didnt work
I'm writing some Rails code for a partial view, and I want it to only show a comment field if somebody is already logged onto a site here.
If the page is viewed by someone who isn't a member of the site yet, the shared/comment_not_logged_in fragment should be passed in.
However, I'm totally stumped as to why I can't run the same check to decide if the page should add the class attribute "missing_your_voice" to the enclosing div element here:
<li class="user_submission_form bubble comment_form <% "missing_your_voice" if not current_user %>">
<% if current_user %>
<%= image_tag(current_user.avatar(:comment), :class => "profile_pic") %>
<% form_for [parent, Comment.new] do |f| %>
<%= render "comments/form", :f => f %>
<% end %>
<% else %>
<%= render :partial => 'shared/comment_not_logged_in' %>
<% end %>
</li>
The same idiom, "missing_your_voice" if not current_user returns the string in irb, and also in the console debugger.
What am I doing wrong here?
You forgot an =. Replace <% by <%=, so that you get:
<%= "missing_your_voice" if not current_user %>
Remember that <% ... %> will only run Ruby code, but not display anything. Using <%= ... %> will run the code and display the result of the expression.
As molf already pointed out, there's a missing = on your view.
It should be <%=.
Other than that, be sure to make your controller method available to your view by calling helper_method in your controller.
Take a look on the documentation if needed.