Rendering across tabs - ruby-on-rails

I am using Bootstrap in my rails app and in particular, I would like to extend Bootstrap's tabbable functionality in the following sense:
Each user of my site can have multiple profiles, namely model User has_many profiles, and I would like to display these profiles across tabs in their user/show page. Each profile has a name attribute which I want to display on the tab itself.
I was previously rendering these profiles using the typical conventions:
On user/show page:
<%= render 'shared/prof %>
shared/_prof:
<% if #prof_items.any? %>
<ol class="profiles">
<%= render partial: 'shared/prof_item', collection: #prof_items %>
</ol>
<%= will_paginate #prof_items %>
<% end %>
shared/_prof_items
<li id="<%= prof_item.id %>">
...content...
</li>
users_controller:
...
def show
#user = User.find(params[:id])
#profiles = #user.profiles.paginate(page: params[:page])
#profile = #user.profiles.build
#prof_items = #user.prof.paginate(page: params[:page])
...
end
...
So essentially I would like the outputted html to be like this:
<div class="tabbable" style="margin-bottom: 5px;">
<ul class="nav nav-tabs" id="tabHeaders">
<li class="active">
Name of first profile
</li>
<li>
Name of second profile
</li>
#and so on for each prof_item
</ul>
<div class="tab-content" id="tabContent">
<div class="tab-pane active id="<%=prof_item.id%>">
#render first profile item
</div>
<div class="tab-pane" id="<%=prof_item.id%>">
#render second profile item
</div>
</div>
However I'm not sure what code I need in user/show.html.erb, what code I need in shared/_prof_item.html.erb, and what code I need in shared/_prof.html.erb.

You can simplify your code by using rails facility to render collections:
<%= render partial: 'shared/prof_item', collection: #prof_items) %>
<%= render partial: 'shared/profile', collection: #profiles) %>
shared/_profile.html.erb: # use local variable profile
<div class="tab-pane active" id="<%=profile.id%>">
#render first profile
</div>
shared/_prof_item.html.erb # use local variable prof_item
<li>
Name of second profile item
</li>

Related

Ruby on rails : filter data by attributes, without call instance variable many times

I have a bootstrap tabs, with 3 tabs : verified comments, unverified comments and all comments. Each tab-content displaying different comments depending to the RoR attribute :verified (true, or false). But I have a optimization problem, because I call #ratings (comments) 3 times, and filter the comments like that : #ratings.where(:verified => true), and in production the application is really slow.
Someone know how could I filter verified, unverified and all with an other method ?
here is the code :
show.html.erb
<% if #school.ratings.count > 0 %>
<ul class="nav nav-tabs" role="tablist" style="margin-bottom: 30px;">
<li role="presentation" class="<%= 'active' if #school.is_subscribed? %>">
<a href="#verifie" aria-controls="verifie" role="tab" data-toggle="tab">
<h3>
Avis vérifiés (<%= #ratings.where(:verified => true).count %>)
</h3>
</a>
</li>
<li role="presentation">
<a href="#non_verifie" aria-controls="non_verifie" role="tab" data-toggle="tab">
<h3>
Avis non-vérifiés (<%= #ratings.where(:verified => false).count %>)
</h3>
</a>
</li>
<li role="presentation" class="<%= 'active' unless #school.is_subscribed? %>">
<a href="#all_avis" aria-controls="all_avis" role="tab" data-toggle="tab">
<h3>
Tous les avis (<%= #ratings.count %>)
</h3>
</a>
</li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane <%= 'active' if #school.is_subscribed? %>" id="verifie">
<%= render partial: "schools/rating", collection: #ratings.where(:verified => true) %>
</div>
<div role="tabpanel" class="tab-pane" id="non_verifie">
<%= render partial: "schools/rating", collection: #ratings.where(:verified => false) %>
</div>
<div role="tabpanel" class="tab-pane <%= 'active' unless #school.is_subscribed? %>" id="all_avis">
<%= render partial: "schools/rating", collection: #ratings %>
</div>
</div>
<a href="#post-rating" id="post-rating-bottom-btn" class="btn btn-warning post-rating-btn"><%= fa_icon 'star' %>
Laisser un avis</a>
<% else %>
<p>Pas encore d'avis sur cette auto-école. Soyez le premier
<a id="post-rating-be-first" href="#post-rating" class="btn btn-warning post-rating-btn"><%= fa_icon 'star' %>
Donnez votre avis</a>
</p>
<% end %>
schools_controller.erb
#school = School.where(city_namespace: params[:city], title_namespace: params[:title]).first || raise(ActionController::RoutingError.new('Not Found'))
#rating = Rating.new(params[:rating])
#rating.school_id = #school.id
#ratings = #school.ratings.desc(:created_at)
Don't hesitate if you need more code / infos !
Thanks !
I am personally not sure whether you're spending your server resources on multiple calls of the instance variables. I believe that your app is slow because you select the large amount of ActiveRecord objects (which are slow themselves). So my tips are:
In your controller you can simply initialize 3 instance variables:
#ratings = #school.ratings.desc(:created_at)
#verified_ratings = #ratings.where(:verified => true)
#unverified_ratings = #ratings.where(:verified => false)
Then just call them in your view. It's not about optimization but still.
Tip 0. If you need to select a few Rating records use pluck. With pluck you will not instantiate te ActiveRecord object but the arrays of arrays. Pass to pluck the symbols of columns you need to get. For example:
Rating.where(:verified => false).pluck(:column_1, :column_2)
Tip 1. To save your server resources you can use pagination to select data from DB by portions. Use Kaminari or will_paginate gems.
Tip 2. If you're on Postgresql use postgresql_cursor gem to perform something like:
Rating.desc(:created_at).each_instance(block_size: SIZE_YOU_NEED).lazy.map { do_something }
It will load your data in batches. The size of batches can be changed (pass the value to block_size). You'll be able to sort the data in those batches (without postgresql_cursor gem you'll not be able to do that). One could say it's an alternative approach to pagination.

undefined method 'each' when accessing articles from navbar in rails app

I need to be able to access articles through the navbar dropdown.
in my views/application/_navbar.html.erbI have the code snipped below. It is not working and I always get this error undefined method 'each' for nil:NilClass
when I hit the link in the dropdown the app goes to the right path http://localhost:3000/lcas/1
I only need to be able to access the first article in each category so http://localhost:3000/lcas/1 is the right path for the first link in the dropdown
code from views/application/_navbar.html.erb
<ul class="dropdown-menu">
<% #lcas.each do |lca| %>
<li><%= link_to lca.title, lca_path(lca) %></li>
<% end %>
<% #energy_analyses.each do |energy_analysis| %>
<li><%=link_to energy_analysis.title, energy_analysis_path(energy_analysis) %></li>
<% end %>
<% #green_accountings.each do |green_accounting| %>
<li><%= link_to green_accounting.title, green_accounting_path(green_accounting) %></li>
<% end %>
<li class="divider"></li>
<li>Something</li>
<li class="divider"></li>
<li>something</li>
</ul>
On my index.html.erbI have basically the same code and there it works.
<% #lcas.each do |lca| %>
<div class="col-md-4 col-xs-12">
<span class="glyphicon glyphicon-tasks icons" aria-hidden="true"> </span>
<div class="panel-heading">
<h3 class="panel-title"><%= link_to lca.title, lca_path(lca) %></h3>
</div>
</div>
<% end %>
I've tried to add
#lcas = Lca.all
#energy_analyses = EnergyAnalysis.all
#green_accountings = GreenAccounting.all
to the application_controller.rb but with out any success.
here is the lcas_controller.rb
class LcasController < InheritedResources::Base
private
def lca_params
params.require(:lca).permit(:title, :body, :image)
end
end
It would be very nice if someone could guide me through this.
You probably need few things:
Set a before_action method for those variables in application_controller.rb to make sure each other controller calls it and pass the variable to their corresponding view:
before_action :set_vars
def set_vars
#lcas = Lca.all
#energy_analyses = EnergyAnalysis.all
#green_accountings = GreenAccounting.all
end
Now you should be able to access #lcas from your partial, but using instance variables in partials is violating MVC, so you should pass locals to it:
<%= render 'application/navbar', :lcas => #lcas %>
Not sure how to pass multiple locals, maybe like this:
<%= render 'shared/navbar', locals: {:lcas => #lcas, :energy => #energy_analyses, :green => #green_accountings} %>
And of course, don't use # in partial if passing locals:
<% energy.each do |energy_analysis| %>

How to use yield with dashboard sidebar to show contents on same page

I have a rails app with a functionality quite similar to yelp with a dashboard for customers where they can edit their place but also change their acccount settings, etc. A customer can only have one place for now.
I've created a dashboard controller, views with a partial for the sidebar and everything is working so far but my problem is that when clicking on a link in the sidebar it yields to the application.html.erb. I want to yield everything from the sidebar to the main part of the dashboard/index.html.erb
So my question is how do I yield the things I click in my sidebar to the part on the right next to the sidebar on the page. Basically the functionality is like a navbar on top (only for logged in customers) but I get confused with two yields. I tried "content_for" and <%= yield :sidebar %> but didn't figure out how to get it working yet. Also I am using devise with a user and customer model which share the views and have the functionality for the customer to edit his user account in the dashboard sidebar which might cause a problem with "content_for"?
Please note that I am still learning ruby on rails and am very happy for any kind of input!
dashboard_controller.rb
def index
#place = Place.where(customer_id: current_customer.id).first
end
dashboard/index.html.erb
<div class="content">
<div class="sidebar">
<%= render 'dashboard/sidebar' %>
</div>
<div class="main">
<%= yield %>
</div>
</div>
_sidebar.html.erb
<li class="nav-link">
<%= link_to "<span class='fa fa-cog'></span> Edit".html_safe, edit_place_path(#place) %>
</li>
<li class="nav-link">
<%= link_to "<span class='fa fa-cog'</span> Settings".html_safe, edit_customer_registration_path %>
</li>
<li class="nav-link">
<%= link_to "<span class='fa fa-sign-out'></span> Log Out".html_safe, destroy_customer_session_path, method: :delete %>
</li>
application.html.erb
<body>
<%= render 'layouts/shared/header' unless #disable_navbar %>
<%= yield %>
<%= render 'layouts/shared/footer' unless #disable_footer %>
</body>
Within the context of a layout, yield identifies a section where content
from the view should be inserted.
http://guides.rubyonrails.org/layouts_and_rendering.html#understanding-yield
So no - yield will not "yield" to your dashboard/index.html.erb. Rather if you want it to have a different layout you should create a layout.
Lets look at an example:
<% # app/views/layouts/application.html.erb %>
<body>
<nav id="top-menu">
<h1><%= link_to 'MyApp', root_path %></h1>
<ul>
<%= yield :nav_links %>
<%# lets provide some default content for nav_links %>
<% content_for :nav_links do %>
<li><%= link_to 'Products', products_path %></li>
<% end %>
</ul>
</nav>
<div id="main">
<%= yield %>
</div>
</body>
Here we create a named yield in the layout called :nav_links and also add some "default" content with content_for :nav_links.
So lets look at what happens when we render /products/index.html.erb:
<div class="products">
<% if #products.any? %>
<%= render #products %>
<% else %>
<p>No products are available at this time</p>
<% end %>
</div>
<%# we also want to add a contextual navigation link %>
<% content_for(:nav_links) do %>
<li><%= link_to 'Sales', sales_path %></li>
<% end %>
The rendered result is:
<body>
<nav id="top-menu">
<h1>MyApp</h1>
<ul>
<li>Products</li>
<li>Sales</li>
</ul>
</nav>
<div id="main">
<div class="products">
<p>No products are available at this time</p>
</div>
</div>
</body>
Rails inserts the content from the view into the "main" layout and the content from content_for(:nav_links) is concatenated into the buffer.
Also I am using devise with a user and customer model which share the
views and have the functionality for the customer to edit his user
account in the dashboard sidebar which might cause a problem with
"content_for"?
The only problem you may have is a "namespace collision" - if you are using yield :sidebar and a gem for example is also using the same name for a yield you may have unexpected results.

Render an attributes of each object - Rails

I have a view right now that renders an object on the page. The object is an Integration. On the Integration object I have attribute called filters. Filters are stored as an array. All I need to do is list out the filters of each integration below them in a list. Here is my code.
View
<% if #integrations.any? %>
<div class="configured-integrations">
<h3 class="heading-3">My Configured Integrations:</h3>
<ul class="integration-list integration-list--compact">
<%= render #integrations %>
</ul>
</div>
<% end %>
Screenshot
In the screenshot you can see that each of those elements are integrations. I need to list the filters of each integration below the title there.
Controller
def index
# Get the list of the user's integrations grouped first by provider then
# from oldest to newest."
#integrations = current_account.integrations
.order(type: :asc, created_at: :asc)
end
I hope this is clear enough. So recap: I need to list the filters on each integration below. I've already tried stuff like this #integrations.first.filters but that wont work because it's a static call. I need something like a list. Thank you
You can add another partial to render all filters which are associated with your Integration.
Create a partial file _show_filters.html.erb in your views
<% filters.each do |filter| %>
<li><%= filter %></li>
<% end %>
And render this partial while iterating through your #integration object like this.
<% if #integrations.any? %>
<div class="configured-integrations">
<h3 class="heading-3">My Configured Integrations:</h3>
<ul class="integration-list integration-list--compact">
<% #integrations.each do |integration| %>
<li>
<%= integration %>
<ul class="">
<%= render 'show_filters', filters: integration.filters %>
</ul>
</li>
<% end %>
</ul>
</div>
<% end %>
What you need to iterate through each integration, then <%= render integeration.filters %>
<% if #integrations.any? %>
<div class="configured-integrations">
<h3 class="heading-3">My Configured Integrations:</h3>
<ul class="integration-list integration-list--compact">
<% #integrations.each do |integration| %>
<li>
<%= integration %>
<ul class="">
<%= render integration.filters %>
</ul>
</li>
<% end %>
</ul>
</div>
<% end %>
You will have to update this code to make the partials work, but i hope this gets the idea across.
You can't use the shortcut <%= render #integrations %> here, because you want a subgroup inside #integrations. So you'll have to do it the long way.

dynamic bootstrap tabs with rails

I am trying to get rails to generate dynamic navigation tabs that refer to groups user is enrolled at. Basically, what I want to achieve is to dynamically have tabs named after groups that user is enrolled at (which is working fine) and then showing the content of each group by clicking on its tab (which is not working properly for some reason). The page loads data correctly but toggling between tabs doesn't work
Here is the code in the view
<div class="tabbable tabs-left">
<div class="row">
<div class="col-md-4">
<ul class="nav nav-pills nav-stacked">
<% current_user.group.each do |group| %>
<li><a href="#<%= group.name %>" data-toggle="tab">
<%=group.name %></a></li>
<% end %>
</ul>
</div>
<div class="col-md-8">
<div class="tab-content">
<% current_user.group.each do |group| %>
<div class="tab-pane fade <%= 'in active' if current_user.group.first == group %>" id="<%=group.name%>">
<% if current_user.group_feed(group.id).any? %>
<ol class="microposts">
<%= render current_user.group_feed(group.id) %>
<%= group.name %>
</ol>
<% end %>
</div>
<% end %>
</div>
</div>
</div>
</div>
Is there something that I am missing?
The problem is group.name producing an invalid html id attribute.
html ids should not start with a number(numbers anywhere else are ok), and have no spaces. Example:
Invalid:
1foo
aaa b
Valid:
foo1
aaa-b
group.name.parameterize will remove any odd chars(#£$ etc) and replace spaces with "-" so use that.
You also want to make this unique as things with names like: "foo" and "foo!" will parameterize to the same thing: "foo".
I'd go with:
id="<%=(group.name.gsub(/[0-9]+/, "")+group.id.to_s).parameterize%>"
This code, removes any number from the name(it only really applies at the start of the id) then adds the id on the end making it unique.

Resources