how to use jQuery in Rails helper - ruby-on-rails

I am trying to use the tabs function from jQuery UI in a Rails app. I am using a helper for the navigation and I would like to keep it that way. The code in my helper is:
def links_for_navigation
html = ""
html = <<HTML
<ul>
<li>Courses</li>
<li>Parts</li>
<li>Categories</li>
</ul>
<div id="tabs-1"><% link_to "Courses", courses_path %>
<div id="tabs-2"><% link_to "Parts", parts_path %>
<div id="tabs-3"><% link_to "Categories", categories_path %>
HTML
end
My view pulls in the code with <% links_for_navigation %>
I added to my application.js:
jQuery(function() {
jQuery("#tabs").tabs();
});
And my application.html.erb has:
<%= stylesheet_link_tag 'courses', 'jquery-ui-1.8.13.custom.css' %>
<%= javascript_include_tag :defaults %>
<%= javascript_include_tag 'jquery-1.5.1.min.js', 'jquery-ui-1.8.13.custom.min.js', 'application' %>
When I try to load the page I get cannot find string HTML before EOF. What am I doing wrong?

I recommend using a partial, rather than a helper method. The partial, I'll call it _nav.html.erb, would look like the following (note the id="tabs" on the surrounding div):
<div id="tabs">
<ul>
<li>
Courses
</li>
<li>
Parts
</li>
<li>
Categories
</li>
</ul>
<div id="tabs-1">
<%= link_to "Courses", courses_path %>
</div>
<div id="tabs-2">
<%= link_to "Parts", parts_path %>
</div>
<div id="tabs-3">
<%= link_to "Categories", categories_path %>
</div>
</div>
Then in the appropriate view file, you can insert the partial with a call to render:
<%= render :partial => 'nav' %>
I've never used jQuery tabs but try adding this to application.js, based on this example. The "#tabs" selector inside $() corresponds to the id="tabs" from above:
$(function() {
$( "#tabs" ).tabs();
});
I'll offer a couple tips/reminders as well:
Embedded ruby (ERB) tags needs to have an = if you want them to print something, i.e. <%= #user.name %> versus <% #user.name %>. The first will output a string into the file, the second will not. There are cases where you might not want to print something, so you would leave off the =.
Be sure to close all your HTML tags. Rails' ERB files can get messy anyways, so proper HTML structure is key.
Avoid using HTML outside of *.html.erb files. This isn't a golden rule, but it's a good rule of thumb.

Related

In Ruby on Rails how to apply active state to navigation depending on the page?

So I have a nav partial that's rendering on the application that every other page is inheriting. Then in the application it's looking for body content to render. The navigation links are something like:
<ul class="ul-class">
<li class="li-class">One</li>
<li class="li-class">Two</li>
<li class="li-class">Three</li>
</ul>
EDIT:
I cannot adjust the li or a tag as they're being pulled down from our CMS. The only thing I can affect is added a class to the ul or the li.
What I'm trying to do is if a person clicks and goes to page Two that Two is then marked active with a border on the bottom. CSS like this:
.nav-active {
border-bottom: 5px solid #d9d9d9;
padding-bottom: 21px;
}
I know I have some jQuery code that will do this (minus some border issues) but I'm trying to do it the Rails way.
For reference the application file has:
<body>
<%= render partial: 'shared/nav' %>
<%= content_for?(:body) ? yield(:body) : yield %>
<%= render 'shared/footer' %>
</body>
Then in two file I have:
<%= content_for :head do %>
<%= stylesheet_pack_tag 'styles/two' %>
<% end %>
<%= content_for :body do%>
<h2>Words going here</h2>
<% end %>
Navigation is present but I'd like for Two to be active. I've tried something like this and it did nothing:
<%= if current_page?('two') then '.nav-active%>
<%= content_for :head do %>
<%= stylesheet_pack_tag 'styles/two' %>
<% end %>
<%= content_for :body do%>
<h2>Words going here</h2>
<% end %>
Is there a better/actual way to apply CSS to the current page in Rails?
EDIT: Also tried the following:
<%= 'nav-active' if current_page?('/two') %>
This kind of worked but not really. It's not really applying a class but the word nav-active.
You can define a helper like this:
module ApplicationHelper
def current_class?(test_path)
return 'nav-active' if request.path == test_path
''
end
end
And use it in your erb template:
<li class="li-class <%= current_class?('/two') %> ">Two</li>
A more rails way would be to use the link_to helper:
<%= link_to "Two", '/2', class: current_class?('/two') %>
There is also an active_link_to gem which wraps link_to helper, with the facility to inject configurable conditions when the link would be considered active.

Show/hide model contents in the context of other models

so I have a Category model with a "has many" relationship to my Soup model.
Currently, I have my page rendering a list of Categories with the Soups within each below. The page output looks like this:
Ramen
*Soup 1
*Soup 2
Other Soups
*Soup 3
*Soup 4
I added the ability to click on the Category name to show/hide the Soups. But I'd like to have this functionality's scope limited to each Category. In other words, I'd like to have clicking "Ramen" show/hide Soup 1 and Soup 2 only. Right now, clicking any Category shows/hides all 4 Soups.
views> categories> index.html.erb
<ul id="folderList">
<% #categories.each do |category| %>
<li>
<img src="https://cdn4.iconfinder.com/data/icons/small-n-flat/24/folder-blue-128.png" alt="folder" width="10%">
<%= link_to category.name, '#', id: 'show_catcontents' %> (<%= category.soups.count%>)
<div id="catcontents">
<ul>
<%- category.soups.each do |soup| %>
<li><%= soup.name %></li>
<%- end %>
</ul>
</div>
</li>
<% end %>
</ul>
<script>
$(function() {
$('a#show_catcontents').click(function(event){
event.preventDefault();
$('div#catcontents').toggle();
});
});
</script>
Any and all help is appreciated
An easy way to do this while not changing all that much is to add an id to each of your elements to further specify them in your embedded ruby, and then hide that specific element rather than the whole catcontents <div> in your Javascript hide function.
For example:
<div id="catcontents">
<ul>
<%- category.soups.each do |soup| %>
<li id= <%= soup.name %> ><%= soup.name %></li>
<%- end %>
</ul>
</div>
And then of course just change your Javascript hide function to hide by the <li> id rather than the catcontents <div>.

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.

Using multiple yields to insert content

I am trying to insert content on my page with yield but every time action removes whole content from the page. I have one main yield which is working fine:
<body>
<%= render 'layouts/header' %>
<div class="container">
<%= yield %>
<%= render 'layouts/footer' %>
</div>
</body>
But inside that new content which is displayed on one page I have another yield:
<div class="container">
<%= render 'admins/menu' %>
<%= yield :admin %>
</div>
When user clicks on the menu which is rendered, new content should be displayed below that menu.
admins/_menu.html.erb
<div class="navbar">
<div class="navbar-inner">
<div class="container">
<ul class="nav">
<li><%= link_to "Users", :controller => "admins", :action => "test" %></li>
<li><%= link_to "1", ... %></li>
<li><%= link_to "2", ... %></li>
<li><%= link_to "3", ... %></li>
</ul>
</div>
</div>
</div>
Controller:
class AdminsController < ApplicationController
def index
end
def test
#users = User.paginate(page: params[:page])
end
end
test.html.erb
<% content_for :admin do %>
<h1>All users</h1>
...
<% end %>
When I click on the option 'Users' from menu, page refreshes, menu disappears and nothing is displayed inside `body'. I want the content to be displayed below menu. How to use that second yield and accomplish this functionality?
I hope the question is not confusing. If question is confusing, please write me in comments and I will edit it immediately.
Thank you :)
So, when you go to the index page you will get the piece of html that will be placed in the main layout, and this piece of html look like this:
<div class="container">
<%= render 'admins/menu' %>
<%= yield :admin %>
</div>
This code will yield :admin properly.
When you go to the test page you do not have this html code anymore (since it only belongs to the index method). So, anything you put in the content_for(:admin) block will be ignored since no-one is printing it.
What you probably want to do is creating a shared layout for all your admin pages. Follow this guide and you'll have your solution.
Solution
Edit the application.html.erb layout using this:
<%= content_for?(:content) ? yield(:content) : yield %>
instead of
<%= yield %>
Then create an admins.html.erb file inside the layouts folder to handle your admin pages' layout. Something like this:
<% content_for :content do %>
  <div class="container">
<%= render 'admins/menu' %>
<%= yield %>
</div>
<% end %>
<%= render template: "layouts/application" %>
Will do fine. Then in the index.html.erb and test.html.erb just place regular HTML content, without using the content_for(:admin) block. Everything should work fine and you'll have your custom admin template, with a slightly different look from regular pages.
Calling yield doesn't work in helper modules, while content_for does, so you should replace your yield calls in the helper files.
Also noteworthy: using provide is recommended over content_for when you're only using the method in 1 place instead of multiple places. You'll get better performance since it won't leave the buffer open while looking for more content, and your intent will be clearer to other developers that may see your code. (see http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html#method-i-provide)
I found that you have to add an yield (without actually outputing) before the namespaced tags.
<div>
<% yield %>
<div class="mt-3">
<div class="text-2xl tracking-wide font-bold text-gray-900">
heading
<%= yield :heading %>
</div>
</div>
<div class="relative bg-white rounded-xl shadow-xl mb-8 min-h-28">
<%= yield %>
</div>
...

Rails add text to class in a content_tag_for :li

I have a content tag that is creating jquery sortable output.
Some items I don't want sortable, so I've added the following to the jquery:
cancel: ".ui-state-disabled"
So, now I need to put "ui-state-disabled" into the li class.
Currently the code creating the li is this:
<% wostatus.workorders.each do |workorder| %>
<%= content_tag_for(:li, workorder) do %>
<div class="<%= workorder.type.maximo_no %> <%= workorder.priority %> ">
<a href="<%= workorder_path(workorder) %>">
<strong><%= workorder.wonum %></strong>
<%= workorder.description %>
</a>
</div>
<% end %>
<% end %>
The results in HTML are:
<li class="workorder" id="workorder_36">
<div class=" ">
<a href="/workorders/36">
<strong>13-39870</strong>
Added some text again
</a>
</div>
</li>
In the browser, if I edit the li class to include "ui-state-disabled" it works the way I want.
Now, how can I insert the "ui-state-disabled" into the li status if the workorder.wostatus.id = 232 ??
Thanks for your help!!
Make a helper:
def disabled_workorder_li(workorder)
{:class => "ui-state-disabled"} if workorder.id == 232
end
Then in the view:
<%= content_tag_for(:li, workorder, disabled_workorder_li(workorder) %>
Here's what it would look like if you skipped the helper and tried to do it all in the view:
<%= content_tag_for(:li, workorder,
(workorder.id == 232) ? {:class => "ui_state_disabled"} : nil %>
That looks terrible. Putting it into a helper will also make it easier to test.

Resources