I have seen similar questions answered but I cannot find exactly my problem and I can't work out how to change the others to make them fit.
I am writing a simple rails 3 app that has 2 main sections. One called Students and one called iConnects with multiple pages for each. I have a simple navigation bar to allow navigation to either by navigating to the first record of either of these sections when clicked. This click also sets a class which has a colour set on it in CSS to indicate which section you are on. This however only works if you click that link once. If you navigate to any of the sub-pages e.g. students/2 the class is obviously not present there and there is no visual indication.
My question is how do I have a persistent class added to the navigation to indicate whether the user is on either students or iConnects?
I have a separate partial in my shared which I am pulling in with this code..
<div id="section_navigation">
<%= link_to 'iConnect', #iconnects.first, :class => current_page?(#iconnects.first) ? "current" : "" %>
<%= link_to 'Students', #pages.first, :class => current_page?(#pages.first) ? "current" : "" %>
</div>
And from my students show.html.erb
<%= render :partial => 'shared/navigation' %>
I am fairly new to rails and so can't think how to do this myself and I am resisting the urge to do it in a Javascript way as I know backend output is the correct approach for this.
Any hints or tips are more than welcome. Thank you for your time...
Use something like simple-navigation
Related
We have a simple app which has a horizontal layout (left hand side panel and content on the right hand side), with a header and footer. So if you click on a certain object on the left hand side, the view is rendered on the right hand side with navigation panel in the header and footer links. The layout actually renders content on the same page itself for any action on the left hand side and the contents of the left hand side will differ based on the section chosen in the header. How should we go about designing the routes in these cases, which differs from the basic navigation where every action is rendered on a different page.
My routes looks like this..
resources :foos do
resources :foo_bars do
end
end
I would need to show all foos on the left hand side panel and if the user selects a foo it needs to show properties of foo and foo_bars in a table on the right hand side panel. How will the view look for me and how will the URL at the browser look for me? We will have several tabs at the top and based on that you will show foos or similar top level objects
The routes remain the same. You would need to ajaxify your calls.
If your question is:
how should we go about designing routes
The way you have it is just fine if you want to utilize nested resources, and in your case it seems logical.
http://guides.rubyonrails.org/routing.html#nested-resources
Currently, your url will be as follows: /foos/:foos_id/foo_bars/some_action
Let me rename these so things make more sense. Lets say foos is categories, and foo_bars is actions.
Personally, I would override the to_param in the categories model file.
to_param
#return a more readable attribute here
name
end
In this way, your URL would be more closely tied with the names of all the categories on the left side of your page.
So now, if you had a record in the categories table which had the name animal, your URL would look like this: /categories/animal/actions/some_action
That seems pretty logical to me. Make sure in your controller you fetch the record via the proper attribute if you use to_param.
I would apply the same principal to the nested resource as well, then your whole URL would be accurately representing what tab is selected on the page. If you had a record in actions with the name "running", and you had things setup properly, then you could have your url look similar to: categories/animal/actions/running.
You could play around with all the options in your routes file, then use rake routes in terminal to see what changes and what your urls will look like before you even touch the browser.
Here are some extra resources for you.
http://apidock.com/rails/ActiveRecord/Integration/to_param
http://guides.rubyonrails.org/action_controller_overview.html
Hope this helps.
There is no good answer to your question - it all significantly depends on the layout of your application. Besides, there are valid answers here about to_param, and using AJAX, that add important details. But, to give you a head start.
For your views/foos, rewrite your index.html.erb as:
<%= render partial: "show_foos", locals: { foos: #foos, selected_foo: nil }%>
And your show.html.erb as:
<%= render partial: "show_foos", locals: { foos: #foos, selected_foo: #foo }%>
In your foos_controller.rb in show method you need to obtain both #foos and #foo, e.g.:
#foos = Foo.all
#foo = Foo.find(params[:id])
Now, to the fun part. Back to views/foos directory. Create a partial called "_show_foos.erb" (the one that we called both from #index and #show). Do something like:
<table>
<tr>
<td>
<%= render partial: "show_foos_list", locals: { foos: foos, selected_foo: selected_foo }%>
</td>
<td>
<%= render partial: "show_foo_props", locals: { selected_foo: selected_foo }%>
</td>
</tr>
</table>
Please note that's an extremely brute & ugly example that creates a table with two columns: one for the list of foos in the left "panel", the other for displaying the results for the selected foos in the right "panel". In real life use divs and styling. Also, consider pushing the layout to where it belongs - to the appropriate layout file - and using named yields there. But, as I said, a headstart - simple table.
Now, just define the two partials mentioned here. First, the "_show_foos_list.erb" that lists the foos on the left. Assuming each foo has a 'title' attribute, something like:
<% foos.each do |foo| %>
<%= link_to_unless selected_foo && (foo.id == selected_foo.id), foo.title, foo %><br />
<% end %>
Second, the foo & foo_bars on the right - "_show_foo_props.erb":
<% if selected_foo %>
# Here display the Foo attributes
<h2> Foo: <%= selected_foo.title %> </h2>
<% selected_foo.foo_bars.each do |foo_bar| %>
# Here display each FooBar that belongs to Foo
<h3>FooBar <%= foo_bar.title %></h3>
<%= foo_bar.description %>
<% end %>
<% end %>
Again, very crude example. Replace 'title', 'description' with the right sets of parameters, use partials to display FooBars. Do the styling with CSS. Etc, etc, ... Refactor as you see fit.
Talking about the routes. What you get is when you go to your "www.yourapp.com/foos" url is the list of all foos on the left, nothing on the right. Once you press on any foo in the left column, you go to "www.yourapp.com/foos/:id", where :id is the ID of the selected foo (and consider to_param from the other answer here or more advanced techniques to make this part meaningful) and get the list of foos on the left, and the properties of the selected foo and all foo_bars belonging to it on the right.
Hope that helps to start laying out your own implementation based on the rough idea presented here.
If I understand your question correctly, the answers saying Ajax is required are not correct. I have an ancient Perl app (written in 1999) that does this. Am currently re-implementing in Rails, and it's working fine. Frames make it particularly easy to allow the data to scroll while the menu stays fixed.
You do need to use HTML4 frames, which are deprecated in HTML5, It's possible to use an IFRAME for the data rendering frame and be HTML5 compliant, but the result is less usable than the FRAME solution in HTML4, at least with some browsers.
As others have said, your routes are fine.
The trick is to use the target field in the form to direct the Submit response to the rendering frame. My haml code for the "command" frame is
= form_tag admin_menu_path, :method => :put, :target => 'data_frame' do
...
The rest is just a normal form. This form remains constant in (my case) the left frame while responses replace each other in the right data_frame.
The matching frame HTML is:
<frameset cols="360,*">
<frame name="menu_frame" src="...">
<frame name="data_frame" src="admin.htm">
</frameset>
You would have to use an outer frameset to get the header and footer, but this should be straightforward.
I am ready for comments saying frames are far from best practice. But for this particular application, they are perfect: simple, understandable, and extremely browser independent. E.g. my 1999 Perl generated code ran fine on IE 2.0 and Netscape (the ancestor of Firefox, friends). And it's still perfect on every modern browser I can find. Wish Ajax could say the same...
If I've misunderstood your question, I'll happily delete this response.
I would like to have a right side bar with content changes for each page.
For example, when I am in Friends page, the side bar should display New Friends.
When I am in Account page, the side bar should display Recent Activities.
How should I go about this to respect Rails design patterns? I heard about Cells gem, but I am not sure if I use it.
here is one way, in your layout add a named yield section
<div id="main-content">
<%= yield %>
</div>
<div id="side-content">
<%= yield(:side_bar) %>
</div>
Then in your views put content into the named yield using content_for
# friends view ....
<% content_for(:side_bar) do %>
<%= render :partial => "shared/new_friends" %>
<% end %>
# account view ....
<% content_for(:side_bar) do %>
<%= render :partial => "shared/recent_activity" %>
<% end %>
this requires you to be explicit about what content appears in the side bar for every view,
maybe having it do it dynamically is better? probably depends on the specific situation and your preference
see also - http://guides.rubyonrails.org/layouts_and_rendering.html#understanding-yield
I came by this question in a moment of a big design change in our views. After thinking about the sidebar problem a bit, I realized that there's no best solution (as always). There are better solutions for each case.
I'll compare 3 solutions here:
using content_for(:sidebar) and yield(:sidebar)
using the partials approach
using the Cells gem
1. Using content_for(:sidebar) and yield(:sidebar)
This is good for cases when each link (each controller action) you access renders a different sidebar. In this case, each view you access will have the content_for(:sidebar) part.
If your sidebar view depends only on the state of some variable in the session, for example, the sidebar should not be rendered for every link you access.
Then you should use a good caching system like turbolinks, to avoid rendering many times the same thing, or use something like the Cells gem with a javascript to render only the main part of the layout.
2. Using partials
Using partials is always good to eliminate duplication. If your sidebar is very simple and is changed for every controller, you can render it as a partial. But if you're rendering different partials in the same controller, according to some state, it may be an indication that you have business logic in your views, which should be avoided.
3. Using the Cells gem
Very good design pattern when you have to render your sidebar from a different controller than the rest of the view each time.
It takes a lot of business logic out of the view, which sure is a good practice.
Here you have an action calling a view. Inside that view, there is a statement render_cell(:sidebar, params). This statement will do some business logic and render the view of the sidebar. It's as if the first action called other controller actions to render specific parts of your view (called cells)
If you make changes to the sidebar only, you may have to create other simple action, so that a javascript will request it. This action will call the render_cell(:sidebar) method again to respond with the view.
It's a very interesting approach.
Other ideas:
Your sidebar could be rendered only with javascript from the same
action.
Your sidebar could be rendered by an angular controller, and rails sends jsons with the sidebar objects. (look for "One page apps")
try something like this
<div class="sidebar">
<% if current_page?(controller => "friends", :action => "show") %>
<h4>New Friends</h4>
<% elseif current_page?(controller => "accounts", :action => "show") %>
<h4>Recent Activities</h4>
<% end %>
</div>
If the above code fits what you are trying to do(looks like this is what you want to achieve), then stick with it, else it may be beneficial to go with some gems. Also checkout helper page on how to use current_page? method. Hope it helps
I'm working on a small picture application. That I'm trying to do is build a counter to track how many times each image is clicked.
Right now I have in my view:
<% #galleries.each do |g| %>
<% for image in g.images %>
<div id="picture">
<%= render 'top_nav'%>
<%= link_to g.source, :target => true do %>
<%= image_tag image.file_url(:preview) %>
<% g.vote %>
<% end %>
<%= will_paginate(#galleries, :next_label => "Forward", :previous_label => "Previous") %>
</div>
Obviously this doesn't work, as the g.vote executes every time it's rendered, not clicked. Here's the vote method in my model:
def vote
self.increment!(:score)
end
I'm looking for a solution to run the vote method only when the image above is clicked. The links are to external resources only, not to a show action. Should I be building a controller action that's accepts a post, executes the vote, then redirects to the source?
Anyway, looking for some ideas, thanks.
I've done something similar, but keeping a count of how many times a Download link was clicked. This was awhile ago and I didn't know about Ajax at the time, but now I would recommend using jQuery (a great library in my opinion, but you could use something else) and do an Ajax call when the image is clicked that would execute some controller action which would increment that vote.
The other way, which is what I did in my scenario, and is what you talked about there, is creating a custom action in the controller that accepts a post. But I have to ask as well, does clicking on the image do something else in the behaviour of your website? For example, if when you click the picture, another random image is supposed to come up, that means you'll already have an action to load a new image and it be easy to stick the vote up in there before showing a new image. Otherwise you'd have to create the new controller action. If that's the case, the Ajax would be more efficient as the user wouldn't see a momentary flash as the page was refreshed (especially bad if the refresh time is long).
I've had a problem with the following issue in Rails and ASP.Net MVC. Often there are multiple widgets of functionality on a page, yet one controller action is supposed to render the page. Let me illustrate:
Let's say I have a normal e-commerce site, and the menu is made of categories, while the page is to display an group of products.
For the products, let's say I have an action on a controller that looks something like:
def product_list
#products = Products.find_by_category(:name => 'lawnmowers')
end
And I have a layout with something like
<div id="menu"><%= render :partial => 'menu' %></div>
<div id="content"><%= yield %></div>
The products have a view...
<%= render :partial => 'product', :collection => #products %>
(note I've ommited the product view as irrelevant)
And the menu has a partial...
<% Category.each {|c| %>
<%= render :partial => 'menu_node', :locals => { :category => c } %>
<% } %>
The line I have a problem with is the "Category.each.do" in the view. I'm fetching data in the view, as opposed to using variables that were set and bound in the controller. And it could easily be a more complex method call that produces the menu.
The solutions I've considered are:
-A view model base class that knows how to get various pieces of data. But you could end up with one of these for each conceptual "section" of the site.
-a local variable that populates at the top of each method (violates DRY)
-the same thing, but in a before_filter call
None of these seem very elegant to me. I can't help but look at this problem and think that a MVP presenter per view (not screen) is a more elegant solution.
ASP.Net MVC has render action (different from rails render :action), which does address this, but I'm not sure what I think of that solution.
Thoughts? Solution suggestions?
Added Note:
The answers provided so far are good suggestions. And they apply to the example I gave, where a menu is likely present in every layout, and is clearly secondary to the product data.
However, what if there is clearly no second class citizen? Portal type sites commonly have multiple unrelated widgets, in which each is important.
For example, What if this page was displaying weather trends, with widgets for temperature, humidity, and precipitation (and each is a different model and view type).
In rails we like to have a concept of thin-controllers, thick-models. So I think you're right to not want to have variables set in the controller.
Also, in order to enable a more-complex method later on, I recommend doing something like:
/app/controllers/application_controller.rb
before_filter :add_menu_nodes
def add_menu_nodes
#menu_nodes = Category.menu_nodes(current_user)
end
/app/views/layouts/application.html.erb
<%= render :partial=>:menu, :locals=>{:categories=>#menu_nodes} %>
/app/models/category.rb
def self.menu_nodes(current_user)
Category.all.order(:name)
end
That way in the future you could update Category.menu_nodes with a more complicated solution, based on the current user, if you need.
Forgive me if I butcher the Ruby (or misunderstand your question), but what's wrong with
class section_helper
def menu( section )
// ...
menuBuiltAbove
end
end
in the view
<%= section_helper.menu( 'section' ) %>
?
I'm developing a simple rails app for my own use for learning purposes and I'm trying to handle 2 models in 1 form. I've followed the example in chapter 13 of Advanced Rails Recipes and have got it working with a few simple modifications for my own purposes.
The 2 models I have are Invoice and InvoicePhoneNumber. Each Invoice can have several InvoicePhoneNumbers. What I want to do is make sure that each invoice has at least 1 phone number associated with it. The example in the book puts a 'remove' link next to each phone number (tasks in the book). I want to make sure that the top-most phone number doesn't have a remove link next to it but I cannot figure out how to do this. The partial template that produces each line of the list of phone numbers in the invoice is as follows;
<div class="invoice_phone_number">
<% new_or_existing = invoice_phone_number.new_record? ? 'new' : 'existing' %>
<% prefix = "invoice[#{new_or_existing}_invoice_phone_number_attributes][]" %>
<% fields_for prefix, invoice_phone_number do |invoice_form| -%>
<%= invoice_form.select :phone_type, %w{ home work mobile fax } %>
<%= invoice_form.text_field :phone_number %>
<%= link_to_function "remove", "$(this).up('.invoice_phone_number').remove()" %>
<% end -%>
</div>
Now, if I could detect when the first phone number is being generated I could place a condition on the link_to_function so it is not executed. This would half solve my problem and would be satisfactory, although it would mean that if I actually wanted to, say, delete the first phone number and keep the second, I would have to do some manual shuffling.
The ideal way to do this is presumably in the browser with javascript but I have no idea how to approach this. I would need to hide the 'remove' link when there was only one and show all 'remove' links when there is more than one. The functionality in the .insert_html method that is being used in the 'add phone number' link doesn't seem adequate for this.
I'm not asking for a step-by-step how-to for this (in fact I'd prefer not to get one - I want to understand this), but does anyone have some suggestions about where to begin with this problem?
There is a counter for partial-collections:
<%= render :partial => "ad", :collection => #advertisements %>
This
will render "advertiser/_ad.erb" and
pass the local variable ad to the
template for display. An iteration
counter will automatically be made
available to the template with a name
of the form partial_name_counter. In
the case of the example above, the
template would be fed ad_counter.
For your problem of detecting whether a row is the first one or not, you could add a local variable when calling the partial:
<%= render :partial => 'mypartial', :locals => {:first => true} %>
As it would be much easier to detect in the main file, whether a row is the first or not I guess.
Instead of detecting whether a phone number is the first, you could also detect whether a phone number is the only one. If not, add remove links next to all numbers otherwise, do not display the remove link. Note that besides showing/hiding the link, you also need to add code, to prevent removing of the last number by (mis)using an URL to directly delete the number instead of using your form.