Creating content for rails-based applications - ruby-on-rails

I'm facing a problem of cleaning up my application in Ruby on Rails. What I have is a pretty standard 3-panel, header and footer layout where different parts of the screen contain different functionality.
By that I mean for example that the header contains (among others) a select that allows one to select parts of the application and a context-dependent menu. The main content area contains obviously the most interactive stuff whereas side panels contain quick-links with stuff like shopping-cart preview, list of potentially attractive products for the customer, a selector to narrow down the list of options...
I was wondering how do I go about simplifying the design. Right now I have the stuff that provides data for the "common" stuff (as opposed to direct content that's placed in the center) called from all the actions (with a filter) but that doesn't feel right for me. I've read that "components" are also not the way to go for obvious performance reasons.
Is there something that's more like component-oriented (other frameworks do have that kind of stuff - Grails: <ui:include ../>, ASP.NET MVC: <% Html.RenderAction() %>)?
Best regards, Matthias.

You can use content_for for managing different location of a layout.
layout.html.erb
<body>
<div id="header">
</div>
<div id="content">
<div id="main">
<%= yield %>
</div>
<div id="side_bar">
<%= yield :side_bar %>
</div>
</div>
<div id="footer">
</div>
</body>
In your views use content_for to assign content to various sections of the layout:
<% content_for :side_bar do %>
<div>
<h1> Popular Posts </h1>
</div>
<% end %>
<div>
<h1> All Posts </h1>
<% #posts.each do |post| %>
<h2> <%= post.name %> </h2>
<% end %>
</div>
Reference:
Railscast
Edit 1
You can use partials to create reusable screen sections.
views/shared/_shopping_cart.html.erb
<div>
display the shopping cart details
<div>
views/posts/index.html.erb
<% content_for :side_bar do %>
<%= render :partial => 'shared/shopping_cart' %>
<% end %>
Essentially you can use:
Filters to set data
content_for to set layout
partials to display data
Combination of these three should allow you to componentize your solution.

layout
In app/views/layouts, a global layout application.html.erb defines the outer most layout (e.g. your 3-panel design).
The application layout can be overrided by a controller layout. For example, if you have a Tasks controller, you can add a tasks.html.erb in the app/views/layouts folder. The tasks.html.erb will be your default layout for any Tasks views (index, add, edit, etc.)
In application.html.erb or tasks.html.erb, you may add two partials, e.g. _header.html.erb, _side.html.erb. Those two partial will serve as the default panels for the views.
A better tutorial is at http://guides.rubyonrails.org/layouts_and_rendering.html

Related

How to display music in a category when a category is clicked (without a page refresh) in Ruby on Rails?

I am building a website for a music database.
I would like to display categories like classical and hip hop first; when the user clicks on a category, I would like to display all the tracks names in that category (I would like to do this without a page refresh). I would also need to support pagination of this list displaying 10 items in one page (and I need to display buttons for display, edit, delete alongside each track in this list)
How can I accomplish this? Does bootstrap provide components that support this kind of implementation?
(If I could do this with a page refresh, I have done this before and I would know how to do it. But I am not sure how to do this without a page refresh)
On views/categories/index.html.erb:
<% #categories.each do |category| %>
<%= link_to category_path(category), :remote => true do %>
<%= category.name %>
<% end %>
<div class="category_<%= category.id %>">
</div>
<% end %>
Then create a partial views/categories/_show.html.erb (here you can insert a table or a structure with bootstrap row and columns):
<% #category.tracks.each do |track| %>
<%= track.name %>
... and you can add here delete and edit actions
<% end %>
Then create views/categories/show.js.erb:
$(".category_<%= #category.id %>").html("<%= escape_javascript(render("show")) %>");
Fyi what you are trying to do has nothing to do with bootstrap, unless you wanted for instance display tracks on a bootstrap modal.
Here is a poor man's bootstrap example I whipped up: when you click on the Track Category example, the table should expand. (You must have bootstrap v4 set up for this to work) and you must have a track_table partial as well - you can use the same partial for the two different tables that you want:
<div class="track-group">
<div class="track track-default">
<div class="track-heading">
<h4 class="track-title">
<p data-toggle="collapse" href="#collapse1"> Track Category (Click to Expand)</p>
</h4>
</div>
<div id="collapse1" class="track-collapse collapse">
<div class="track-body">
<%= render 'track-table' %>
</div>
<div class="track-footer">
</div>
</div>
</div>
</div>
Here's how it looks with an existing app on my computer:

DRY view for sidebar

In my quest to keep my application views as DRY as possible I've encountered a little snag. My appliation.html.erb incorporates a static sidebar menu. Each of my main controllers incorporates a secondary sidebar menu (essentially a submenu). I can take the code that renders the menu out of application.html.erb and put it in each of my views and change the secondary sidebar there, but this produces a lot repetition in my views.
I saw this SO post and looked at this page, but I was unable to get either idea to work. I was thinking that I could put something like:
<% provide(:submenu, 'layouts/sidebars/sidebar_customers_contacts') %>
at the top of each view and use that to render the associated partial by doing
<% content_for(:submenu) do %>
<%= render :partial => :submenu %>
<% end %>
from the application.html.erb but of course that didn't work.
This is my current application.html.erb:
<div class="side">
<%= render 'layouts/sidebar' %>
<%= render 'layouts/sidebars/sidebar_dashboard' %><!-- this needs to load a sidebar based on the controller that calls it. Each view of the controller will get the same sidebar. -->
</div>
<div class="main-content">
<%= yield %>
</div>
I feel like I'm making this more difficult than it really is. Is there a simple way to do this?
Rails provides a helper called controller_name which you can read more about here.
Assuming you adhere to your own naming conventions, this should work as-is. If you decide some controllers don't get a sidebar, you may need to throw in some conditionals...
application.html.erb
<div class="side">
<%= render "layouts/sidebar" %>
<%= render "layouts/sidebars/#{ controller_name }" %>
</div>
<div class="main-content">
<%= yield %>
</div>
EDIT
Sorry, my mistake was using single quotes instead of double-quotes. You cannot use #{string interpolation} within single quotes. Source

Application.html.erb template inheritance

I have a situation in rails (version 4.04, ruby version 2.1) where I've been using the standard application.html.erb to define the main framework for my site, header, footer, nav bar, etc. When I got to an inner div, call it, inner-content, thats where I put a <% yield %> statement so that the sub template can take over and place its content in the correct place (for example products#show or products#index have show.html.erb and index.html.erb respectively which just the content for those actions).
The problem is I realized I was duplicated some code in those sub templates. In ever one of them (except one) I always was starting off like this:
<div class="columns large-6 medium-6 center-small">
<div class="inner_wrapper">
And I was always ending like this:
</div>
</div>
So I was thinking, I shouldn't be repeating all this code. I should move this into application.html.erb so that every template automatically gets the inner-content set up correctly.
The problem is that one action I was talking about. There is one action that has a different setup. I don't want to have to type in those extra 2 divs for every sub-layout except one. Is there a better way to do this?
One way could be to check which controller your currently using this in your application.html.erb
<% if params[:controller] == "controller name" %>
<div>
<%= yield %>
</div>
<% else %>
<div class="different div">
<%= yield %>
</div>
<% end %>
Not sure if this the best way, but its one way to do it.
Create a different layout file and call it maybe products_layout.html.erb.
Then in the controller
class ProductsController < ApplicationController
layout: 'products_layout'
....
end
Or do it on a per action
def show
render 'show', layout: 'products_layout'
end
http://guides.rubyonrails.org/layouts_and_rendering.html

How can I use the same layout multiple times on a page?

TL; DR: How can I use the same layout multiple times on a page? All attempts to render partials with recurring layouts go into the elements where the first time the layout is used.
Quick Info:
Using
Rails 2.3.14
Ruby 1.8.7
My partials use content_for, which is pretty handy with layouts.
Basically, my layout looks like this:
<div class="header">
<%= yield :modal_header %>
</div>
<div class="body">
<%= yield :modal_body or yield %>
</div>
Generally, this is how I use my modal layout:
<% content_for :modal_header do %>
header text / elements
<% end %>
<% content_for :modal_body do %>
body info / settings for object or warning message, etc
<% end %>
This is how I render modals on my page:
<%= render :partial => "section_options", :layout => "modal" %>
and that works really well
BUT
when, on the same page, I try to render another modal:
<%= render :partial => "section_content_options", :layout => "modal" %>
This is what happens to the modal at the top of my page (occurs first in the HTML document)
<div class="header">
-- header from second partial --
-- header from first partial --
</div>
<div class="body">
-- body from second partial --
-- body from first partial --
</div>
and then later in the page, where the content from the second partial is supposed to be:
everything is rendered correctly ...
For the sake of this example:
<div class="header">
section content options
</div>
<div class="body">
section contents options ... options
lil confirm / cancel buttons
</div>
Is there a way to fix this? This behavior really messes with javascript bindings as well. so. yup.
Is there a bug with layouts in rails 2.3.14? do I have to upgrade to rails 3 to get rid of this problem?
Modals should be rendered using partials, not layouts. Layouts are designed as a way of defining the overall layout of the entire site, not specific parts. Something like a modal (which by design is specific) should be a partial that's rendered either through a condition or JavaScript.

How would I go about adding a simple menu to a Rails Application? (How to use yield :menu)

I want to add a menu to my First Ever Rails Application. Nothing too complicated.
I've worked out that I should maybe reference it from application.html.erb, but after that I'm stuck.
Here's what I've got so far (It's not much)
<%= render :partial => "menu" %>
If I'm rendering a partial call "menu" in application.html.erb, where do I put the menu file, and what do I call it? Does it need to go in the controller of the view?
Can I call this partial from whichever layout subfolder I'm in?
Part II. If I want to show different content according to the view I'm in - how do I do this?
<body>
<p>[<%= yield :menu %>]</p>
<%= yield %>
</body>
</html>
I'm just learning Rails, so sorry about the stupid questions. Also, I'm interested in not only a solution, but also an idea of best practices.
In basic terms you are looking for the content_for helper. You put this inside your views which will then populate named blocks in partials or layouts such as :menu. You may elect, if you wish, to use partials to actually define the content for the content_for regions.
In a view:
<% content_for :menu do %>
<ul>
<li> ... </li>
<li> ... </li>
</ul>
<% end %>
or as:
<% content_for :menu do %>
<%= render :partial => "some_menu_content" %>
<% end %>
In the layout or partial:
<div id="menu">
<%= yield :menu%>
</div>
Watch this screencast from the Railscasts series for more information. It's old but still applicable

Resources