Rendering Partial onclick in Rails - ruby-on-rails

I am trying to render a partial onclick to save on page loading time.
<ul id="pop_twitter">
<% #topic_count.each do |k,v| %>
<% #k = k %>
<!-- make a link that onclick renders a partial below -->
<li><% link_to_remote k, ?????? %></li>
<ul id="twitter_<%= k %>" style="display:none;">
<!-- where the partial should load -->
</ul>
<% end %>
</ul>
I need to load the partial 't_tweets' and it also need the variable 'k' in from the .each loop.

here's a rough start:
link_to_remote("Show", :url => {:action => 'show_tweets', :k => k})
EDIT: i guess since you're only showing a partial, it would make sense to also pass :method => :get, since link_to_remote defaults to :post.
and in your controller
def show_tweets
render :update do |page|
page.insert_html :bottom, 'pop_twitter', :partial => 't_tweets', :locals => params[:k]
page["tweet_#{params[:k]}"].visual_effect :highlight #using scriptaculous
end
and your partial
<div id="<%= "tweet_#{k}" %>">
<%= tweet.body %>
</div>

Related

error while rendering a partial in rails3

I am trying to render a partial in rails using the normal
<%=render 'subtasks/descform %> command
Now the issue is that this descform partial is being called from inside another partial which is 'subtasks/subtask' and their are many subtasks.
So the subtask partial is also getting rendered from the task partial like this
<% task.subtasks.each do |subtask| %>
<%=render :partial=>subtask %>
<%end%>
So the problem is in the desc form,it has a form like this
<%=form_for subtask,:remote=>:true,:method => :put do |f| %>
<%= f.text_area :description,rows:'5',cols:'12',:class=>'myarea'%>
<%= f.submit "save",class:'btn btn-success col-md-4' %>
<a data-id='<%=subtask.id %>' href="#" class="myclose"><i class="fa fa-times fa-lg" style="margin:10px auto;"></i></a>
<% end %>
So,when i am trying to render this form i am getting an error
undefined local variable or method `subtask' for #<#:0x458f6b8>
Can somebody please explain the cause of this error and how to fix this.
You just need to pass the variable to the partial, like so:
<%=render 'subtasks/descform', :subtask => subtask %>
Then it will be accessible in your partial as subtask
Pass the subtask object as a local variable to descform partial. First to subtask partial and then to descform
<% task.subtasks.each do |subtask| %>
<%=render :partial => 'subtask', :locals => {:subtask => subtask} %>
<%end%>
And inside subtask partial, pass it on to descform
<%=render :partial => 'subtasks/descform', :locals => {:subtask => subtask} %>
You have to mention partial when you pass in locals. For eg, the following will not work
<%=render 'subtasks/descform', :locals => {:subtask => subtask} %>
In the task partial:
<%= render :partial => "subtask", :collection => task.subtasks %>
In the subtask partial:
<%=render :partial => 'subtasks/descform', :locals => {:subtask => subtask} %>

Rails: 'Load More' button with Ajax & Kaminari

I would like to create a "load more" ajax pagination, with Kaminari.
I'm using this code :
class BienvenueController < ApplicationController
def index
#articles = Admin::Article.page(1).per(2)
respond_to do |format|
format.html
format.js
end
end
end
# Bienvenue#index
<div class="container" style="min-width:<%= #width %>px">
<%= render "shared/articles" %>
<%= link_to_next_page #articles, 'Load More', :remote => true, :id=>"load_more_link" %>
# Shared/articles
<% #articles.each do |a| %>
<article class="<%= a.rubrique.color %>">
<div class="sharing">
<%= image_tag "facebook-32.png" %>
</div>
<p class="color<%= a.rubrique.color %>"><i>Le <%= I18n.localize(a.created_at, :format => :long) %> par David Perrotin</i></p>
<h1><%= a.titre %></h1>
<div class="excerpt">
<%= a.chapo.html_safe %>
</div>
<div class="image">
<%= image_tag a.mainphoto.url(:medium), :width=>"100%" %>
</div>
<div class="contenu">
<%= a.contenu.html_safe %>
</div>
<div class="readmore">
<%= link_to "Continuer la lecture", article_path(a) %>
</div>
</article>
<% end %>
# index.js.erb
$('.container').append("<%= escape_javascript(render 'shared/articles')%>");
$('#load_more_link').replaceWith("<%= escape_javascript(link_to_next_page(#articles, 'Load More', :remote => true, :id=>'load_more_link'))%>");
But the problem is that when I click on "Load More", it always shows the two same articles, the partial is never refreshed with two more articles, like I would like.
I just ran into an issue with this that might help others. Depending on your version of jQuery, don't use replaceWith on the #load_more_link in index.js.erb.
There is a regression (http://bugs.jquery.com/ticket/13401) that an empty replaceWith does nothing, so on the very last page of your set, the link_to_next will be empty, making the line: $('#load_more_link').replaceWith(''); and thus will not replace the last "more" button, so you'll continually load the last page of your data set.
Fixed by updating jQuery version or use empty().html('...') instead of replaceWith.

How do I implement a show all comments feature in ruby on rails?

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

Rails generate extra <li> tags automatically

this is the code :
<ul >
<% items.each do |item|%>
<%= render :partial => "somepartial", :locals => { :title => item.title} %>
test_text
<% end %>
</ul>
the partial:
<li><a><%= title %></a></li>
and the out put is :
<ul >
<li><a>item1</a></li>
<li>test_text</li>
<li><a>item2</a></li>
<li>test_text</li>
<li><a>item3</a></li>
<li>test_text</li>
</ul>
< li > tags around the test_text is extra. Partial and the model is not related, so do not suggest me to use collection method. When partial is rendered inside the each loop, rails does not put li tags around it, but the anything except the partial gets li tags around them.
The question is not entirely clear to me, so maybe i should refrain from answering.
But, i would propose to use haml, which gives you much cleaner views.
Your main view would become:
%ul
= render :partial => "items/item", :collection => items
and your partial items\_item.html.haml would look like this
%li
%a
= item.title
I don't see a real link inside your li-item, so maybe you want something like:
%li
= link_to item.title, item_path(item)
Instead of this:
<% items.each do |item|%>
<%= render :partial => "items/item", :locals => { :title => item.title} %>
<% end %>
Try this:
<%= render :partial => "items/item", :collection => items %>

Rails render partial with block

I'm trying to re-use an html component that i've written that provides panel styling. Something like:
<div class="v-panel">
<div class="v-panel-tr"></div>
<h3>Some Title</h3>
<div class="v-panel-c">
.. content goes here
</div>
<div class="v-panel-b"><div class="v-panel-br"></div><div class="v-panel-bl"></div></div>
</div>
So I see that render takes a block. I figured then I could do something like this:
# /shared/_panel.html.erb
<div class="v-panel">
<div class="v-panel-tr"></div>
<h3><%= title %></h3>
<div class="v-panel-c">
<%= yield %>
</div>
<div class="v-panel-b"><div class="v-panel-br"></div><div class="v-panel-bl"></div></div>
</div>
And I want to do something like:
#some html view
<%= render :partial => '/shared/panel', :locals =>{:title => "Some Title"} do %>
<p>Here is some content to be rendered inside the panel</p>
<% end %>
Unfortunately this doesn't work with this error:
ActionView::TemplateError (/Users/bradrobertson/Repos/VeloUltralite/source/trunk/app/views/sessions/new.html.erb:1: , unexpected tRPAREN
old_output_buffer = output_buffer;;#output_buffer = ''; __in_erb_template=true ; #output_buffer.concat(( render :partial => '/shared/panel', :locals => {:title => "Welcome"} do ).to_s)
on line #1 of app/views/sessions/new.html.erb:
1: <%= render :partial => '/shared/panel', :locals => {:title => "Welcome"} do -%>
...
So it doesn't like the = obviously with a block, but if I remove it, then it just doesn't output anything.
Does anyone know how to do what I'm trying to achieve here? I'd like to re-use this panel html in many places on my site.
While both of those answers above work (well the example that tony links to anyway) I ended up finding the most succinct answer in that above post (comment by Kornelis Sietsma)
I guess render :layout does exactly what I was looking for:
# Some View
<%= render :layout => '/shared/panel', :locals => {:title => 'some title'} do %>
<p>Here is some content</p>
<% end %>
combined with:
# /shared/_panel.html.erb
<div class="v-panel">
<div class="v-panel-tr"></div>
<h3><%= title -%></h3>
<div class="v-panel-c">
<%= yield %>
</div>
</div>
Here's an alternative based on previous answers.
Create your partial on shared/_modal.html.erb:
<div class="ui modal form">
<i class="close icon"></i>
<div class="header">
<%= heading %>
</div>
<div class="content">
<%= capture(&block) %>
</div>
<div class="actions">
<div class="ui negative button">Cancel</div>
<div class="ui positive button">Ok</div>
</div>
</div>
Define your method on application_helper.rb:
def modal_for(heading, &block)
render(
partial: 'shared/modal',
locals: { heading: heading, block: block }
)
end
Call it from any view:
<%= modal_for('My Title') do |t| %>
<p>Here is some content to be rendered inside the partial</p>
<% end %>
You can use the capture helper, and even inline in the render call :
<%= render 'my_partial',
:locals => { :title => "Some Title" },
:captured => capture { %>
<p>Here is some content to be rendered inside the partial</p>
<% } %>
and in shared/panel:
<h3><%= title %></h3>
<div class="my-outer-wrapper">
<%= captured %>
</div>
which will produce:
<h3>Some Title</h3>
<div class="my-outer-wrapper">
<p>Here is some content to be rendered inside the partial</p>
</div>
See http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html
Based on the accepted answer this is what worked well for me using Rails 4.
We can render a panel as such:
= render_panel('Non Compliance Reports', type: 'primary') do
%p your content goes here!
This requires a helper method and a shared view:
helper method (ui_helper.rb)
def render_panel(heading, options = {}, &block)
options.reverse_merge!(type: 'default')
options[:panel_classes] = ["panel-#{options[:type]}"]
render layout: '/ui/panel', locals: { heading: heading, options: options } do
capture(&block)
end
end
View (/ui/panel.html.haml)
.panel{ class: options[:panel_classes] }
.panel-heading= heading
.panel-body
= yield
I think it will work (just did quick dirty test) if you assign it to a variable first and then output it.
<% foo = render :partial => '/shared/panel', :locals =>{:title => "Some Title"} do %>
<p>Here is some content to be rendered inside the panel</p>
<% end %>
<%= foo %>

Resources