I have some code (that is working), but I just want to make sure I am putting it in the right place per rails conventions/best practices. The purpose of the code, is to generate a set of subheading navigation links if '#facility' is defined (which could be done in one of several controllers). Right now, I have the following bits of code spread out as identified below.
My issue: this doesn't feel right, particularly the bit of logic at the beginning of the html template. I will also want to define a few other similar helpers to define these subhead links based on different models. Should this logic (of setting #subhead_links) be in the application controller? As a method in each model (so I would set #subhead_links = #facility.subhead_links)?
I've looked some for answers, but this type of philosophical question isn't as readily google-able as an error code.
in application helper
# build subhead
def subhead_links(facility)
links = [
{ :label => "Configure Facility", :path => facility_path(facility) },
{ :label => "Waitlists", :path => waitlist_tiers_path(:id => facility.id) },
{ :label => "Applications", :path => sponsors_path(:id => facility.id) }
return links
end
in a partial included in application.html.erb template
<% if #facility %>
<% #subhead_links = subhead_links(#facility) %>
<% end %>
<% if #subhead_links %>
<nav class="subnav">
<ul>
<% #subhead_links.each do |link| %>
<li><%= link_to link[:label], link[:path] %></li>
<% end %>
</ul>
</nav>
<% end %>
in various other controllers...
#facility = Facility.find(params[:id])
I never rely on instance variables in partials and try to put as much logic as possible in helpers.
Here this would result in the following:
application_layout:
<%= render_subhead_links(#facility) %>
application_helper:
def render_subhead_links(facility)
if facility
links = [
{ :label => "Configure Facility", :path => facility_path(facility) },
{ :label => "Waitlists", :path => waitlist_tiers_path(:id => facility.id) },
{ :label => "Applications", :path => sponsors_path(:id => facility.id) }
render :partial_name, :links => links
end
end
partial:
<nav class="subnav">
<ul>
<% links.each do |link| %>
<li><%= link_to link[:label], link[:path] %></li>
<% end %>
</ul>
</nav>
Related
How can I pass multiple parameters from the view to the controller? I need to perform a request like for example:
GET "/indicators/data?country_id=5&statistic_id=12&anotherparameter_id=anothervalue, ...."
Each parameter is generated by its own link_to selection and I would like to use the parameters together to query my model.
#app/views/indicators/index.html.erb
<!--statistic select button-->
<ul id = "statistics">
<% Statistic.all.each do |stat| %>
<li><%= link_to stat.indicator_name, :action => "data", :controller => "indicators", :statistic_id => stat.indicator_code, remote: true %></li>
<% end %>
</ul>
<!--country select button-->
<ul id = "countries">
<% Country.all.each do |c| %>
<li><%= link_to c.name, :action => "data", :controller => "indicators", :country_id => c.id, remote: true %></li>
<% end %>
</ul>
#config/routes.rb
get 'indicators/data' => 'indicators#data'
#app/controllers/indicators_controller.rb
def data
#country = Country.find(params[:country_id]).name
#statistic = params[:statistic_id]
#queryresult = Mymodel.where(country: #country, statistic: #statistic)
respond_to do |format|
format.js
end
end
EDIT: I had come across answers that advise to do like so:
link_to "Nonsense search", searches_path(:foo => "bar", :baz => "quux")
# => Nonsense search
But in my case I have two different links one with a single parameter corresponding to foo (eg. a checkbox) and the other a single parameter for baz (eg. a dropdown) so to say. The challenge is how to pull the parameters from different links and put them together.
Instead of using controller, action etc you can simply use route_helper_method with params that you want to pass simply.
<li><%= link_to stat.indicator_name, indicators_data_url(countryid: "value", another_id: "value", etc: "etc"), remote: true %></li>
Read about link_to here
I've got an easy one for you guys.
I want to have a featured content section where the current article is EXCLUDED
So this works using Middleman Blog with delete_if:
<% blog(content).articles.delete_if{|item| item == current_article}.each do |article| %>
<%= article_content %>
<% end %>
However I'm using Middleman proxies so I don't have access to the current_article method...
I've got an YAML structure that holds the following mock data (amongst others) with the folder setup like: data > site > caseStudy > RANDOM-ID423536.yaml (Generated by a CMS)
Inside each yaml file you'll find stuff like:
:id: 2k1YccJrQsKE2siSO6o6ac
:title: Heyplace
My config.rb looks like this
data.site.caseStudy.each do | id, this |
proxy "/cases/#{this.slug}/index.html", "/cases/cases-template.html", :locals => { this: this }, :ignore => true
end
My template file holds the following
<%= partial(:'partials/footer', :locals => {:content => data.site.caseStudy, :title => 'See more projects'}) %>
My loop that gets content
<% content.each do |id, this| %>
<%= partial "partials/tile" %>
<% end %>
So I guess my question is, how can I use delete_if in this instance so that the current article isnt being displayed?
I have tried stuff like, but to no avail:
<% content.delete_if{|e| e.title == content.title}.each do |id, this| %>
<%= partial "partials/tile" %>
<% end %>
Any help is appreciated! Thank you!
Solved, ended up doing
<% content.reject{ | id, item| item == this }.each do |id, this| %>
<%= partial "partials/tile", :locals => { :this => this, :dir => "/#{this.content_type_id.downcase}/", :secondary => 'View' } %>
<% end %>
Courtesy of #David Litvak
I'm the maintainer of contentful_middleman. Looks like you could just send the this to the partial as local, then iterate through your content and make sure that you exclude the one that matches the id of this.
I'm implementing show/hide feature for users comments.
Discussed here: https://stackoverflow.com/a/10174194/439688
My aim was to:
1. Limit the default shown comments to 2.
2. Have a span with text that states the number of total comments for that particular micropost and when clicked by a user have it expand and show all comments for that micropost. I would be using Jquery/Ajax to hide, show, prepend etc.
The first change was to limit the amount of comments shown to the user and I achieved this by creating a method in my helper called "comments" and here I pass in the id of the micropost the comment belongs to.
def get_comments(micropost_id)
Comment.limit(2).order("created_at DESC").where(:micropost_id => micropost_id)
end
Now the each loop that loops through each comment will only show the 2 most recent comments.
<<% #microposts.each do |m| %>
<% if m.poster_id.nil? %>
<div class="postHolder">
<nav class="micropostOptions">
<ul class="postMenu">
<li class="deletePost"><%= link_to content_tag(:span, "Delete post"), m, :method => :delete, :confirm => "Are you sure?", :title => m.content, :class => "message_delete", :remote => true %>
</li>
<li class="disableCommenting"><%= link_to content_tag(:span, "Pause commenting"), "2" %></li>
<li class="blockCommenter"><%= link_to content_tag(:span, "Block commenter"), "3" %></li>
<li class="openInNewWindow"><%= link_to content_tag(:span, "Open in new window"), "4" %></li>
<li class="reportAbuse"><%= link_to content_tag(:span, "Report abuse"), "5" %></li>
</ul>
</nav>
<%= link_to image_tag(default_photo_for_current_user, :class => "poster_photo"), current_users_username %>
<div class="post_content">
<div class="post_container">
<div class="mainUserNameFontStyle"><%= link_to current_users_username.capitalize, current_users_username %> - <div class="post_time"> <%= time_ago_in_words(m.created_at) %> ago.</div>
</div>
<%= simple_format h(m.content) %> </div>
<div class="commentsCount">
<%= content_tag :span, pluralize(m.comments.count, 'comment'), :class => "view_all_comments" if m.comments.any? %>
</div>
<% if m.comments.any? %>
<% comments(m.id).each do |comment| %>
<div class="comment_container">
<%= link_to image_tag(default_photo_for_commenter(comment), :class => "commenter_photo"), commenter(comment.user_id).username %>
<div class="commenter_content"> <div class="userNameFontStyle"><%= link_to commenter(comment.user_id).username.capitalize, commenter(comment.user_id).username %> - <%= simple_format h(comment.content) %> </div>
</div><div class="comment_post_time"> <%= time_ago_in_words(comment.created_at) %> ago. </div>
</div>
<% end %>
<% end %>
<% if logged_in? %>
<%= form_for #comment, :remote => true do |f| %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.hidden_field :micropost_id, :value => m.id %>
<%= f.text_area :content, :placeholder => 'Post a comment...', :class => "comment_box", :rows => 0, :columns => 0 %>
<div class="commentButtons">
<%= f.submit 'Post it', :class => "commentButton", :disable_with => "Post it" %>
<div class="cancelButton"> Cancel </div>
</div>
<% end %>
<% end %>
</div>
</div>
From here this is where it gets confusing for me. I got slightly further using link_to but then decided I'd prefer not to have the url to the comments count show in the browser status bar. This is why I switched to using span.. but now it's not quite easy to do what I wish to do as I can't use the link_to/remote => true now.
How do I make it so when a user clicks the comment count span an ajax call is made pointing to:
def load_comments
#load_comments = Comment.where(:micropost_id => params[:id])
respond_to do |format|
format.js { render :load_comments }
end
end
I thought about putting a click function in users.js but how would I pass the params of the micropost that is in the each loop in the code above into users.js? I don't think it's possible.
All my comment posting is done via ajax but because I used forms for these it was so much easier for me to just add remote => true and create some js templates and do something on success of ajax post.
Not sure if I'm even going about this the right way. I'd appreciate some help/advice from more experienced rails programmers.
Kind regards
Rails partial
#Display all the comments based on local passed to this partial
# Initially pass limit as 2(or whatever you want). then on click of span pass limit as nil. then you can check if limit is nil you can query the model without limit specifier.
<% #comments = Comment.custom_find(#your_params) %>
<% #comments.each do |comment| %>
<%= comment.title %>
<% end %>
javascript/jquery
function load_all_comments(id)
{
new Ajax.Updater('show_comments',
'<%=url_for(:controller => "your_controller", :action => "your_action")%>', {
parameters: {'id':id },
method: 'get',
onSuccess: function(request){
div_comments = document.getElementById("partial_comments_list");
div_comments.innerHTML = request.responseText;
}
});
} // you can call this js function on span click. use jquery if you want.
Controller:
Then inside your_action of your_controller, dont forget to render the partial
render :partial => "show_comments", :layout => false
Edit:
you can even pass locals to your partial
render :partial => "show_comments", :locals => {:post => #post}
Using this every time your partial view will get updated, on the basis of locals you pass.
of course this is just an example not a complete code/solution.
There may be better ways. but this worked fine for me.
Another option is to just output all of the comments and hide the ones you don't want to show first. <div class="hidden_comments" style="display:none;"> a comment </div>
Then just have some javascript to show them when the span is clicked?
<script type="text/javascript">
$("#span_id").click(function() {
$('.hidden_comments').show();
});
</script>
This works great if you do not don't have a ton of comments.
If you really want to do it your way, I have done it before but it gets messy.
Put this in your application.js
$('.comment_span').live('click', function () {
$.get(this.data_url, null, update_row, 'json');
return false;
});
Your span would look like this:
<span class="comment_span" data_url="http://website.com/resource/more_comments">
show all comments
</span>
This example returns the data as json, so I used the update_row function to update replace the comments data.
function update_row(data, status) {
$("#comments-table").append(data.html);
};
Here is what my controller looked like:
def more_comments
#comments = Comments.all
if #comments
respond_to do |format|
format.js {
render :json => {
:html => render_to_string(:partial => "comments"),
}.to_json
}
end
end
end
You should do this via your index action.
Pass a param to it to determine if you want to show all comments or just the current set (I'd use will_paginate to handle this.
Haven't looked too deep into your code as I'm on my phone right now, but something like this:
class CommentsController < ApplicationController
def index
If params[:show_all] == "true"
#comments = Comment.all
else
#comments = Comment.where(foo: bar).paginate(per_page: 2, page: params[:page])
end
end
Then you have it respond to JavaScript and send the page param with your Ajax request
I want to ask you about best rails way to create a menu.
I should to make a horizontal menu. Every page belongs to the specific item of this menu and this item should has id="current" when user opens on this page.
<ul>
<li id="current">Home</li>
<li>About us</li>
</ul>
As I understand I should to create a special helper which will create this html markup and use this helper with special parameter on every view of every page. Maybe there is a better rails way to do this?
PS. Update
My solution:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :open_most_important
protected
def open_most_important
#menu = {
:cases => { :name => 'Cases', :link => '/cases'},
:groups => { :name => 'Groups', :link => '/groups' },
:projects => { :name => 'Projects', :link => '/projects' },
:settings => { :name => 'Settings', :link => '/settings' },
:about => { :name => 'About us', :link => '/about' }}
#current_menu_item = :cases
end
The fragment of the layout application.html.erb:
<div id="menu">
<ul>
<% #menu.each do |item, value| -%>
<% if #current_menu_item == item -%>
<li id="current"><%= value[:name] %></li>
<% else -%>
<li><%= value[:name] %></li>
<% end -%>
<% end -%>
</ul>
</div>
After I need to set #current_menu_item for every controller in the before_filter
If I understand correctly you want this menu to appear on every page. If that is the case you can just add it to your layout. If you don't understand layouts read up on them at Ruby on Rails Guides http://guides.rubyonrails.org/getting_started.html#customizing-the-layout
This is how I tend to do it:
<%= menu_item("Home", root_path) {
controller.controller_name == "pages" &&
controller.action_name = "home" } %>
<%= menu_item("About us", page_path("about") {
controller.controller_name == "pages" &&
controller.action_name == "about" } %>
The helper:
def menu_item(name, url, options = {})
if yield
options["id"] = "current"
end
link_to name, url, options
end
The "id" will be "current" if the block passed to menu_item returns true.
The way I handle it is to put this in a navigation partial:
<%= nav_link "All Users", users_path, active_tabs.include?('all_users') %>
<%= nav_link "Add User", new_user_path, active_tabs.include?('add_user') %>
And then this helper at the top of each view:
<%= nav 'nav_partials/users_tabs', 'all_users' %> # one view
<%= nav 'nav_partials/users_tabs', 'add_user' %> # another view
And the helpers:
def nav_link(title, path, active, options = {})
if active
options[:class] ? options[:class] += " active" : options[:class] = "active"
end
link_to(title, path, options)
end
def nav(partial, *active_tabs)
partial = "#{params[:controller]}/nav_partials/#{partial}" unless partial.chars.first == '/'
render :partial => partial, :locals => { :active_tabs => active_tabs }
end
To me, it is elegant, even though there are some things that could be a bit better (like the file path thing in the nav helper, as well as referencing params in the helper). But it works very nicely, and it's very flexible.
I built navigation menu using simple navigation and built nested categories which i display in this menu.
navigation.rb code:
navigation.items do |primary|
Category.roots.map do |root|
primary.item ":root_#{root.id}", root.title, category_path(root) do |secondary|
root.descendants.map do |desc|
secondary.item ":desc_#{desc.id}", desc.title, category_path(desc)
end
end
end
end
The question is: how can i display all the levels of categories in my menu. That code does only with two level nesting.
Thank in advance
Please take a look at how refinery-cms does this.
The _menu.html.erb is close to what you have.
In addition to that it has another partial called _menu_branch.html.erb that renders the submenus of the menu recursively.
https://github.com/resolve/refinerycms/blob/master/core/app/views/refinery/_menu.html.erb
https://github.com/resolve/refinerycms/blob/master/core/app/views/refinery/_menu_branch.html.erb
Code cut from github:
_menu.html.erb
<nav id='<%= dom_id %>' class='<%= css %>'>
<ul>
<%= render :partial => '/refinery/menu_branch', :collection => roots,
:locals => {
:hide_children => hide_children,
:sibling_count => (roots.length - 1),
:apply_css => true #if you don't care about class='first' class='last' or class='selected' set apply_css to false for speed.
} -%>
</ul>
</nav>
_menu_branch.html.erb
<li<%= ['', css].compact.join(' ').gsub(/\ *$/, '').html_safe %>>
<%= link_to(menu_branch.title, main_app.url_for(menu_branch.url)) -%>
<% if (children = menu_branch.children unless hide_children).present? -%>
<ul class='clearfix'>
<%= render :partial => '/refinery/menu_branch', :collection => children,
:locals => {
:apply_css => local_assigns[:apply_css],
:hide_children => !!hide_children
} -%>
</ul>
<% end -%>
</li>
You will have to use recursive if this helps at all, you can see an example here: Recursive Rails Nested Resources