Rails: make scope data persist from index to show page - ruby-on-rails

I have a job index page with a list of jobs. There are "filters" that a user can apply where i use scopes to limit the results based on some type of criteria such as "experience"
when a user clicks on a job in the index page they get to the jobs show page where i have a breadcrumb. if they click on that bread crumb to go back to the jobs index page all of the filters set through the scopes have been wiped out.
a user should be able to 1) set a search filter on the index 2) have the search results trimmed 3) click on a link to the jobs show page 4) be able to click on the breadcrumb navigation back to the jobs index page and have all the scopes still be applied. I have 1-3 working, just can't figure out #4 right now.
I'm not sure how to persist that scope data from the index page once i click through to the jobs show page. I'm guessing if i can store it then i can pass it as a parameter when i'm going back to the index page?
my navigation breadcrumb in the jobs show page
<nav aria-label="breadcrumb" role="navigation">
<ol class="breadcrumb">
<li class="breadcrumb-item"><%= link_to "Search Results", filtered_jobs_path(experience: params[:experience]) %></li>
<li class="breadcrumb-item active" aria-current="page">Job Details</li>
</ol>
</nav>
<% if params[:experience].present? %>
<%= 'test' %>
<% end %>
my scope in the model
scope :by_experience, -> (ex) { where(experience: ex) if ex.present? }
calling scope in the controller which is part of the index action
# scopes
if params[:experience].present?
#jobs = #jobs.by_experience(params[:experience])
end
The jobs index partial that lists the job names
<div class="jobs_index_middle_panels col-md-6">
<!-- displays error message if search term can't be found, ignores nil values -->
<% filter_array = [ params[:experience],
params[:num_days_past], params[:company], params[:search], params[:l] ].compact %>
<% if #jobs.present? %>
<!--kaminari gem helper method-->
<h5 class="text1"><%= page_entries_info #jobs, entry_name: 'job' %></h5>
<% else %>
<h5 class="text1">No jobs found. Try removing the following filters:
<span class="text-danger"><%= filter_array.join(", ") %></span></h5>
<% end %>
<% #jobs.each do |job| %>
<div class="card">
<div class="card-block">
<h4 class="vert-spacing2"><%= link_to job.title, job_url(job.id), class: "text-danger" %></h4>
<div class="vert-spacing1"><%= job.company %></div>
<div class="vert-spacing1"><%= truncate(job.description, length: 200, separator: ' ') %></div>
<b><%= job.city.capitalize %>, <%= job.state %></b>
<span class="pull-right"><%= time_ago_in_words(job.created_at) + " ago" %></span>
<%= number_to_currency(job.salary) %>
</div>
</div><br>
<% end %><!--./jobs-->
<!--pagination: uses kaminari gem -->
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center">
<%= paginate #jobs %>
</ul>
</nav>
</div><!--./jobs_index_middle_panels-->

Commit your filters to the session.
if params[:experience].present?
session[:filter_on_experience] = params[:experience]
end
if session[:filter_on_experience].present?
#jobs = #jobs.by_experience(params[:experience])
end
What you are doing here is changing your scope to filter on the session data instead of the params. If there are params, it will reset your session data for the paramater (name your session[:foo] accordingly). If there is session data it will do the scope. No session data, no scope.
It is worth while to add a clear session data method if the page is refreshed. Otherwise your records will be scoped until the user clears cookies!

Related

display results with a show more link

I'm trying to figure out a way to always show 5 jobs in my Rails app and then have a link that when clicked will show all of the remaining jobs. Should I do something in my jobs_controller where I'm just getting the first 5 and then getting the rest, or is it better to do it in the view somehow?
<!-- company -->
<div class="vertical-space">
<b>Company Name</b><br />
<div class="sub-text">
<% Job.by_company_count.size.each do |name, count| %>
<div class="indent"><%= link_to name, filtered_jobs_path(company: name) %>
(<%= count %>)
<br>
</div><!--./indent-->
<% end %>
</div><!--./sub-text-->
</div>
To pass data to the view, set an instance variable of the controller, in the corresponding action:
#jobs = Job.by_company_count
And then access and iterate #jobs in view:
<% #jobs.each do |name, count| %>
<div class="indent"><%= link_to name, filtered_jobs_path(company: name) %>
(<%= count %>)
<br>
</div><!--./indent-->
I'm assuming that you have a scope by_company_count defined in Job model!

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.

Rendering only authorized (Devise) user data in Rails without excess empty space

I am trying to create a todo app that will allow the user to create lists and then "todo" items under each list. However, I want each user to only be able to see his or her lists. While I've been able to partially solve it using the current_user helper, the index page shows empty space where the other users lists are hidden.
Below please find the code for the index.html.erb page inside my todo_lists views.
<% #todo_lists.each do |todo_list| %>
<div class="index_row clearfix">
<% if todo_list.user == current_user %>
<h2 class="todo_list_title"><%= link_to todo_list.title, todo_list %></h2>
<p class="todo_list_sub_title"><%= todo_list.description %></p>
<p><%= todo_list.user.first_name %></p>
<% end %>
</div>
<% end %>
<div class="links">
<%= link_to "New Todo List", new_todo_list_path %>
</div>
Here's my repo on Github, in case you need to see more of the code: https://github.com/jramoscolon/todo
Is there a way to hide these empty spaces, as well as the non-matching todo items?
Given your current view code, you are indiscriminately emitting <div class="index_row clearfix"> elements, even when the todo_list.user does not match the current_user. Simply move the whole<div> outside the current_user check, like so:
<% #todo_lists.each do |todo_list| %>
<% if todo_list.user == current_user %>
<div class="index_row clearfix">
<h2 class="todo_list_title"><%= link_to todo_list.title, todo_list %></h2>
<p class="todo_list_sub_title"><%= todo_list.description %></p>
<p><%= todo_list.user.first_name %></p>
</div>
<% end %>
<% end %>
This way, all of those empty <div> elements aren't included on the page. This should clean up all that empty space.
If your index view is user specific than the instance variable you want should be user specific as well.
Instead of #todo_lists = ToDoList.all
Use the current_user.todo_lists functionality supplied by your has_many/belongs to

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