I'm porting a php app to rails and am wondering what the best practice for having the most flexibility in a rails partial is. I saw a lot of "you could do it like this"
I'm thinking like this:
<%=render :partial => 'shared/show_info',
:locals => {
:info => x ,
:hide_location => true} %>
where use a :locals even if only one argument.
Any ideas on whether this would be the best way for maximum scalability in the future or why this would not be a good idea?
thx
edit #1
added the shared/_show_info.html.erb - this would be the type of thing I'd be doing :hide_location which takes out the location
<% if !local_assigns.has_key? :hide_location %>
<%=info[:location] %>
<% end %>
Related
I am new to Ruby on Rails and i am working through a few example applications in the O'Reilly Head First Rails book. In one of the examples there is a page made up of three partials. The middle partial is a list of items. There is a link right below this section that, when clicked, should refresh the div containing that partial. The book is running examples based off of Rails 2.3 i believe and i am using Rails 3.1. This is the example that the book is giving me:
routes.rb:
map.connect '/flights/:flight_id/seats', :action=>'flight_seats', :controller=>'seats'
seats_controller.rb:
def flight_seats
#flight = Flight.find(params[:flight_id])
render :partial => "flights/seat_list", :locals => {:seats => #flight.seats}
end
show.html.erb:
<div id="seats">
<%= render :partial=>"seat_list". :locals=>{:seats=>#flight.seats} %>
</div>
<$= link_to_remote("Refresh Seats", :url=>"/flights/#{#flight.id}/seats", method=>"get", :update=>"seats") %>
This example is also using prototype.js since that's what Rails 2.3 came with built in. Rails 3 has jQuery as the default JavaScript library. (not sure if that makes a big difference)
Here is what i have so far. This is getting the contents of the partial correctly, it's just not updating the "seats" div after the AJAX call gets the partial. My code:
routes.rb:
match 'flights/:flight_id/seats' => 'seats#flights_seats'
seats_controller.rb:
def flights_seats
#flight = Flight.find(params[:flight_id])
render :partial => "flights/seat_list", :locals => { :seats => #flight.seats }
end
show.html.erb:
<div id="seats">
<%= render :partial => 'seat_list', :locals => { :seats => #flight.seats } %>
</div>
<%= link_to "Refresh Seats", "/flights/#{#flight.id}/seats", :remote => true %>
Any idea why my <div id="seats"> won't refresh with the updated partial? I'm betting there is but i'll ask anyway, is something wrong with my code?
The :remote => true option is a bit weird if you aren't returning JSON data. You can wrap your HTML in a JSON object, though, which is what I typically do. Or if you want something closer to your existing code something like this should work for you:
<%= link_to "Refresh Seats", "/flights/#{#flight.id}/seats", :class => "refresh-seats" %>
In your javascript somewhere:
$(document).delegate(".refresh-seats", "click", function(e){
e.preventDefault();
$("#seats").load(this.href);
});
What would you recommend as the best way to refactor these two bits of view code?
<%if Supplydetail.find_all_by_isbn_id(#isbn).first.nil? %>
<%else%>
<%if Productavailability.find_by_code(Supplydetail.find_all_by_isbn_id(#isbn).first.productsupply_supplydetail_productavailability).nil? %>
<%else%>
<li><%= Productavailability.find_by_code(Supplydetail.find_all_by_isbn_id(#isbn).first.productsupply_supplydetail_productavailability).value %></li>
<%end%>
<%end%>
and (using formtastic)
%li.tip
= tooltip(:test, :hover)
= f.input :relatedmaterial_relatedproduct_idvalue, :label => "Related ISBN", :as => :select, :collection => Isbn.all, :label_method => :descriptivedetail_titledetail_titleelement_titlewithoutprefix, :value_method => :productidentifier_idvalue
%li.list
= link_to "Edit list", isbns_path
I have examples of each of these about a bazillion times in my app, and would like to know I'm refactoring in the best way before I dive in to this rather huge job.
First of all, an empty if branch in an if/else usually (but not always!) smells bad so don't do that, it just makes your code harder to read and understand.
Also, you're computing Supplydetail.find_all_by_isbn_id(#isbn) and Productavailability.find_by_code(...) twice just to get your <li> output. Don't do that either.
And you might want to push most of that logic into your controller (or maybe a helper depending on where and how often it is used) to cut down on the ERB-noise.
Maybe something like this would serve you (and whoever gets to maintain your code) better; a bit of controller stuff first:
#avail = nil
by_isbn = Supplydetail.find_all_by_isbn_id(#isbn).first
if by_isbn
#avail = Productavailability.find_by_code(by_isbn.productsupply_supplydetail_productavailability)
end
And then in your ERB:
<% if #avail %>
<li><%= #avail.value %></li>
<% end %>
If you're doing a lot of this sort of thing then you could add a Productavailability.for_isbn convenience class method in your model. Then your controller would just need:
#avail = Productavailability.for_isbn(#isbn)
But I wouldn't worry about it until you start repeating yourself.
I'm not familiar with formtastic so I can't help you with that.
I'd like to output a form button:
<% content_tag :button :type => :submit, :class => :positive do %>
<%= image_tag "icons/tick.png"%>
Save
<% end %>
Which should generate:
<button type="submit" class="positive">
<img src="/images/icons/tick.png" alt=""/>
Save
</button>
I have this throughout my site, it's getting messy and I just learned that Rails has helper methods. I was curious. Would it be possible to build a helper method so I could just say something like this in rails
<%= form_button(submit) %>
What do you think? Would this live in the application_helper.rb file?
Something like:
def form_button (type)
if type == 'submit'
<% content_tag :button :type => :submit, :class => :positive do %>
<%= image_tag "icons/tick.png"%>
Save
<% end %>
end
end
To answer your first question: Yes it is possible and that is what the Rails framework encourages: reuse of code (otherwise known as the DRY-principle). Specific to view logic, there is a helper for each and every one of your models. This is the default/convention, though it is not something you have to adhere to. I'll explain more about this in the next paragraph. So yes, put your form_button method into a helper -- not necessarily application_helper.rb.
To answer your second question: You could stick it in application_helper.rb, but there's nothing stopping from you making things a little more logical (i.e., creating a buttons_helper.rb). In Rails 3 specifically, all helpers are available to each and every view (though this has ruffled some feathers). So what you could do instead is create a new helper for yourself (i.e., rails g helper Buttons), specifically for creating your buttons, and put your logic in there.
Take a look at this Railscast. It describes in more detail exactly what I've said above. I had the same question and found it very helpful. http://railscasts.com/episodes/101-refactoring-out-helper-object
If the helper is going to be all around the site then application is probably the best place to put it for helpers.
You could do what you have, you don't need to put the <%%> tags since those are for erb Embeded Ruby which are your view extensions and helpers are just .rb so there is not need for that.
I checked the API and it looks like you don't need to role your own helper for this:
content_tag(:p, "Hello world!")
# => <p>Hello world!</p>
content_tag(:div, content_tag(:p, "Hello world!"), :class => "strong")
# => <div class="strong"><p>Hello world!</p></div>
content_tag("select", options, :multiple => true)
# => <select multiple="multiple">...options...</select>
A better way to do this is apply the image as a background image in you stylesheet or do it inline. You don't need to make a helper for this and BTW what happens if the type is not 'submit'? Do you have other inputs to check for or a default? If not it doesn't make sense to wrap it in a helper.
so you can do this"
<% submit_tag "Save", :class => "your_class", :style => "font-size:10px;"%>
or
<% f.submit "Save", :class => "your_class", :style => "font-size:10px;"%>
I render a partial like so:
<%= render :partial => 'widgets/some_partial, :locals => {:foo => 'bar'} %>
So inside of _some_partial.html.erb I render two more partials like so:
<% #foo.nil? #=> false %>
<%= render :partial => 'widgets/another_partial', :locals => {:foo => foo} %>
`<%= render :partial => 'widgets/another_partial_again', :locals => {:foo => foo} %>`
The foo local variable renders fine in some_partial.html.erb and even in another_partial_again.html.erb. However, the foo variable is inaccessible in another_partial.html.erb even though I explicitly passed it in the render call.
What is happening here?
Thanks for the help.
I had the undefined local variable or method error come up for me too when I was rendering a partial with :locals defined.
However, I had a different issue causing my problem, so I thought I would share my solution in case it helps anyone else. (This page was the first result when I googled this error after all)
Basically just make sure you use :partial => 'path/to/partial' in your call to render.
I.e.
<%= render :partial => 'widgets/some_partial', :locals => {:foo => 'bar'} %>
NOT like I was doing:
<%= render 'widgets/some_partial', :locals => {:foo => 'bar'} %>
Easy for a rails/ruby newbie like me to miss.
Solved. Turns out I was also rendering the same partial from the controller without sending the proper local variables. Thanks anyways!!!
Bumped into this very old question cause I faced the same issue.
Turned out that with Rails 4+ if you are not using collections or layout the correct way is:
# Instead of <%= render partial: "account", locals: { account: #buyer } %>
<%= render "account", account: #buyer %>
As documented here.
I'm in need of a more complete example on how to update a select box based on the results of a second select box in Ruby on Rails. I asked about this already here. I've read through the feedback from that posting, but am not having any luck figuring this out, and I've been trying for hours. Anybody know of a better (and more complete) example?
This is generally handled in Javascript. I don't particularly enjoy coding Javascript, so what I do for this in my application is to use a form_observer (a Rails helper which uses the Prototype Javascript library to watch your form for input changes) and have update a DIV in the HTML containing the second select box, based on the results of an AJAX call. Since AJAX talks to my server, I can write arbitrarily complex logic in Ruby to render the new HTML.
Example code:
#goes in view
<%= Code to render the first list box. %>
<%= render :partial => 'second_list_box_partial', :locals => {:selected = insert_magic_here } %>
<%= observe_field(:first_list_box,
:url => { :action => :second_box_ajax }),
:frequency => 0.5,
:update => :second_list_box_div,
:with => %Q| 'value=' + $('first_list_box').value; |
%>
#goes in controller
def second_box_ajax
first_box_value = params[:value]
#magic goes here
#selected = #more magic
render :partial => 'second_list_box_partial', :locals => {:selected => #selected}, :layout => false
end
#goes in partial
<div id="second_list_box_div">
Actual code to render list box goes here.
</div>