Problem description:
I have a view with set of links:
<% #feeds.each do |f| %>
<div class="feed">
<span class="feed_counts"> <%= f.display_counts %> </span>
<%= link_to "refresh", { :controller => 'Feeds', :action => "refresh_feed", :feed_id => f.id}, :remote => true, :class => 'refresh_feed_link' %>
</div>
<% end %>
Users click on the link and launch next controller method:
def refresh_feed
#feed = Feed.find(params[:feed_id])
#feed.parse
end
Now I want to change the content of the corresponding span-element to #feed.total_count value.
My attempts:
Well, as I know there is a two way's to do it without reloading whole the page:
Way 1:
I can include js in my layout:
<%= render :partial => 'shared/partial_js' %>
and use this code in the partial_js:
<script type="text/javascript">
$().ready(function() {
$('.refresh_feed_link').bind('click', function() {
$(this).closest('.feed').find('span.feed_counts').text('woo');
});
});
</script>
In this case I have '$(this)' and I can find the corresponding 'span' element. But I don't have any possibility to get my #feed varible value.
Way 2:
I can add
respond_to do | format |
format.js {render :layout => false}
end
to my controller and create refresh_feed.js.erb file. In this JS file I can use my variable as <% #feed.total_count %>, but I don't know which of my multiple links was clicked. In the other words the $(this) variable in this file will be (window) and I cannot find corresponding span-element.
Question:
Is there any way to get what I want ?
Thanks in advance.
There are lots of ways to do this. Using the wayds that you described, here's a simple solution for the "which link was clicked" problem: dom_id.
1) Make a partial: app/views/feeds/_feed.html.erb
<%= div_for feed do %>
<span class="feed_counts"> <%= feed.display_counts %> </span>
<%= link_to "refresh", { :controller => 'Feeds', :action => "refresh_feed", :feed_id => feed.id}, :remote => true, :class => 'refresh_feed_link' %>
<% end %>
2) In your view:
<%= render #feeds %>
3) In your refresh_feed.js.erb file:
$('<%= dom_id(#feed) %>').replaceWith('<%= escape_javascript( render #feed ) %>');
There's another way that I personally like better, but it will take a me a little while to write it up, so I'll leave this for you here while I write up the other way.
Second Way
Here's how I do it, using CoffeeScript and HAML because they're easier to type. You can just convert this to plain JS and ERB and it will work the same.
I would setup my routes like so:
resources :feeds do
get "refresh_feed", :on => :member
Assuming you've got a "feed" partial, app/views/feeds/_feed.html.haml:
= div_for feed, :class => 'feed_widget' do
%span.feed_counts= f.display_counts
= link_to "Refresh", refresh_feed_path(f), :class => 'refresh_link'
In any view:
= render #feeds
// or, more explicit:
= render :partial => 'feed/feeds', :collection => #feeds, :as => :feed
Now, in app/assets/javascripts/feeds.js.coffee
# Global Scope for CoffeesScript, ignore for JS
root = exports ? this
# Check page if there are feed widgets on it and initialize a handler for each one.
jQuery ->
if $('div.feed_widget').length
$('div.feed_widget').each ->
new FeedWidget $(this)
root.FeedWidget = (container) ->
#container = container
#refresh_link = #container.find('a.refresh_link')
#feed_counts = #container.find('span.feed_counts')
this.initialize()
root.FeedWidget.prototype =
initialize: ->
self = this
#feed_counts.click (event) ->
event.preventDefault()
$.ajax
type: 'GET'
url: self.refresh_link.attr 'href'
dataType: 'json'
error: (xhr, status, error) ->
console.log error
console.log xhr
success: (data, status, xhr) ->
self.feed_counts.text data.feed_counts
Then in your controller:
def refresh_feed
#feed = Feed.find(params[:id]) #assuming you have a resourceful route to this.
#feed.parse
respond_to do |format|
format.js { render :json => { feed_counts: #feed.counts }, :status => :ok } # use whatever method gets you the feed count here.
end
end
Now you are getting a JSON response that is just the feed count, and you have a single JS listener/handler widget that will automatically show up (and function) and place you render that partial. Cool huh?
Note, the above code is not tested since I don't have your app, so you'll have to refine it for your needs. But ask questions and I'll do my best to answer.
Good luck!
Related
I have a web page with a couple of buttons and each button loads information on the page through ajax. Each button calls the same action in controller. That action, called "load" then calls an ajax file which in turns loads different information on the page depending on which button was clicked.
My buttons in my html file look like this.
<%= button_to "Load Profile", { :controller => "surveys", :action => "load"} %>
<%= button_to "Load Personality", { :controller => "surveys", :action => "load"} %>
<%= button_to "Load Experience", { :controller => "surveys", :action => "load"} %>
The load action in the surveys_controller.rb file looks like
def load
respond_to do |f|
f.js { render 'shared/ajax/info.js.erb' }
end
end
The info.js.erb file looks like this
$('#email').empty().html("<%= j render(:partial => 'shared/survey/tech/profile') %>")
This has worked for me in other places but now the content i need to load differs. I need the "#email" and the "shared/survey/tech/profile" to be a parameter sent from the load action. Can anyone help me?
Since you have the same action and same js.erb file for each button therefore you need to send some data using your buttons in order to differentiate with of the buttons is clicked.
For that you need to follow these steps:
a. Create a post request for your custom method load
post 'surveys/load' => "surveys#load"
b. Send some data through your buttons which will differentiate them:
<%= button_to "Load Profile", { :controller => "surveys", :action => "load", :profile => "yes"} , :method=>:post, :remote => true %>
# this will make you access params[:profile]
c. Check inside your load method which button is being clicked using params:
def load
#profile = params[:profile]
#other params to store values of personality and experience
respond_to do |f|
f.js { render 'shared/ajax/info.js.erb' }
end
end
d. Inside your info.js.erb file you could check which one of the variable is equal to yes and then render accordingly
<% if #profile == "yes" %>
#render your partial
<% elsif #personality == "yes" %>
#render your partial
<% elseif #experience == "yes" %>
#render your partial
<% end %>
On a second thought i think it'll be better if we just separate out this logic inside controller and depending on the params value render different js files like:
def load
#profile = params[:profile]
#other params to store values of personality and experience
if #profile == "yes"
respond_to do |f|
f.js { render 'shared/ajax/profile_info.js.erb' }
end
elsif #personality == "yes"
respond_to do |f|
f.js { render 'shared/ajax/personality_info.js.erb' }
end
end
end
Try something like this and see if it works for you;
<%= link_to "Load Profile", { :controller => "surveys", :action => "load"}, remote: true %>
<%= link_to "Load Personality", { :controller => "surveys", :action => "load"}, remote: true %>
<%= link_to "Load Experience", { :controller => "surveys", :action => "load"}, remote: true %>
respond_to do |form
format.js { }
end
and then;
$('#email').empty().html("<%= j render(:partial => 'shared/survey/tech/profile') %>")
In my project I show last 4 comments on a post and then when someone click on the expand comments link rest of the comments should show. I have following code
<%= link_to demo_expand_comments_path(:post_id => post.id, comment_id => comment_id ), remote: true do %>
This is the link to expand comment.
<div id="comments_container_<%= post.id %>">
<%= render :partial => 'community/comments/comment_box_posts', :collection => post.comments.order('id DESC').limit(4).reverse, :as => :comment %>
</div>
Here I am rendering first 4 comments
Now when someone click on expand comments, expand_comments action is called and in expand_comment.js.erb has following code
$('#comments_container_<%= #post_id %>').prepend('<%= escape_javascript(render :partial => 'community/comments/comment_box_posts', :collection => #comments, :as => :comment) %>');
controller action
def expand_comments
#post_id = params[:post_id]
post = Post.find(params[:post_id])
#comments = post.comments.where('id < ?', params[:comment_id]).order("created_at ASC")
I need the last shown comment_id here
respond_to do |format|
format.js{}
end
end
Now what I need help is that when expand comments action is called I want to send the post id and the last comment id I am showing now.
There are several ways to do this. You can change your "expand comment" link as well as you render comments.
In controller:
def expand_comments
...
#comment_id = #comments.last.id
Then in expand_comment.js.erb:
$('#comments_container_<%= #post_id %>').prepend('<%= escape_javascript(render :partial => 'community/comments/comment_box_posts', :collection => #comments, :as => :comment) %>');
$('#id_of_link').attr('href', '<%= demo_expand_comments_path(:post_id => #post_id, comment_id => #comment_id ) %>');
But it is not way I like.
Don't send comment_id parameter like url parameter. You should send it by ajax request.
Remove comment_id parameter from url, store it like data-attribute:
<%= link_to demo_expand_comments_path(:post_id => post.id), data: {comment_id: #comment_id} do %>
This is the link to expand comment.
...
Note that you have to remove "remote: true" from link_to too.
Send ajax request when link clicking:
$('#id_of_link').click(function() {
$.ajax({
type: "get",
url: $(this).attr('href),
data: { comment_id: $(this).data('comment_id') },
dataType: 'script'
});
return false;
});
And update comment_id attribute when comments rendering:
$('#comments_container_<%= #post_id %>').prepend('<%= escape_javascript(render :partial => 'community/comments/comment_box_posts', :collection => #comments, :as => :comment) %>');
$('#id_of_link').data('comment_id', '<%= #comment_id %>');
I am having a difficult time trying to get my partial to refresh on button press. I want to simply refresh the div, and not the whole page. Here is what I have in the view:
<div id="ajax">
<%= render 'layouts/confessions' %>
</div>
Then in my partial _confessions.html.erb, I have some basic html and two buttons similar to this:
<%= form_tag( { :controller => :confessions, :action => :upvote, :id => conf.id }, { :method => :put } ) do %>
<%= submit_tag 'Like' %>
<% end %>
My confessions_controller.rb:
def upvote
#confession = Confession.find(params[:id])
Confession.increment_counter :upvotes, #confession
respond_to do |format|
format.js
end
end
And finally, upvote.js.erb:
$('#ajax').html("<%= escape_javascript(render(:partial => 'confessions')).html_safe %>");
The action of submitting to my database is working, but the page is now redirecting to /upvote?id=9 (id can be different), instead of just refreshing the div. What am I doing wrong? I am new to Rails, so I could be missing something completely obvious...
EDIT: Here is my folder structure:
My view: views/pages/home.html.erb
My partial: views/layouts/_confessions.html.erb
My Controller: controllers/confessions_controller.rb
My js.erb file: views/confessions/upvote.js.erb
After rake routes
confessions GET /confessions(.:format) confessions#index
POST /confessions(.:format) confessions#create
new_confession GET /confessions/new(.:format) confessions#new
edit_confession GET /confessions/:id/edit(.:format) confessions#edit
confession GET /confessions/:id(.:format) confessions#show
PUT /confessions/:id(.:format) confessions#update
DELETE /confessions/:id(.:format) confessions#destroy
upvote /upvote(.:format) confessions#upvote
downvote /downvote(.:format) confessions#downvote
root / pages#home
Make sure you have this in your application.js
//= require jquery
//= require jquery_ujs
FYI: Rails form_for :remote=>true is not calling js method
Then, change this,
$('#ajax').html("<%= escape_javascript(render(:partial => 'confessions')).html_safe %>");
To:
$('#ajax').html("<%= escape_javascript(render(:partial => 'layouts/confessions')).html_safe %>");
Add :remote => true
<%= form_tag( { :controller => :confessions, :action => :upvote, :id => conf.id }, { :method => :put, :remote=>true } ) do %>
Also - read up on rails routes to see how to setup the route for PUT confessions/upvote.
http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
I'm not sure why the Chosen Plugin is not being called when my form partial is dynamically called.
Relative Gems -
rails, 3.1.0
cancan, 1.6.7
dynamic_form, 1.1.4
simple_form, 2.0.1
jquery-rails
Scenario -
I have a page called /index and you can add a House, Dog or Cat. The page starts out blank but has links on the side that you can click to dynamically bring up one of the forms. These forms are in a their own partials.
DogsController
def new
#dog = Dog.new
respond_to do |format|
format.js
end
end
dogs/_form.html.erb
<%= simple_form_for(#dog, :remote => true, :html => { :class => 'form-horizontal span8' }) do |f| %>
<%= render :partial => "shared/error_message", :locals => { :f => f } %>
<%= f.association :house, :prompt => "Select a House", :input_html => { :id => "house-select", :class => "span4" } %>
<%= f.button :submit, 'Done', :class => 'btn-primary span2' %>
<% end %>
PagesController
def index
#cats = current_user.cats.all
#dogs = current_user.dogs.all
respond_to do |format|
format.html
end
end
pages/index.html.erb
<li><%= link_to "Dog", new_dog_path, :remote => true %></li>
<li><%= link_to "Cat", new_cat_path, :remote => true %></li>
<div id="generate-form">
<div class="ajax-loader"></div>
</div>
dogs/new.js.erb
$("#generate-form").html("<%= escape_javascript(render(:partial => 'dogs/form', locals: { dog: #dog })) %>");
All of this code successfully brings up the chosen form on a link click.
My dog model is the same as the cat one. These are the ones with the Chosen plugin issue. Now why isn't the Chosen plugin detected when either form is brought up?
Answer
dogs/new.js.erb
$("#generate-form").html("<%= escape_javascript(render(:partial => 'dogs/form', locals: { dog: #dog })) %>");
$('.house-select').chosen();
I don't see any call to the chosen plugin in your code sample, but I've had the same issue.
The initial call to $('.chzn-select').chosen() will apply to all DOM elements that are on the page at the time, but not to elements that get added later.
To add the plugin to the new .house-select elements that are inserted via AJAX, add this to the end of dogs/new.js.erb:
$('#generate-form .house-select').chosen();
It's worth noting that you can safely run the jQuery .chosen() function on a select box more than once. It adds its own css classes to track which ones have had chosen applied and will gracefully ignore them on consecutive runs.
I have an erb file named index that has a form in it. As a simple example:
<% form_for #foo, :url => {:action => 'bar'} do |f| %>
<%= f.submit "BAR!" %>
<%end%>
When I click the BAR! button it preforms the actions I expect and it forwards me onto the bar.erb file, displaying the expected output. What I would like to be able to do, however, is to take the generated html from this page and stuff it into the innerHTML of a div on the index page. I assume there is a way but I must ask, is there a way to achieve this? Are there any examples available that would be helpful? Thanks!
You should be able to pass the id of the div to update like so:
<% remote_form_for #foo, :url => {:action => 'bar'}, :update => 'id-of-div-to-update' do |f| %>
<%= f.submit "BAR!" %>
<%end%>
In the controller:
def bar
# your code here
respond_to do |format|
format.html { redirect_to(xxx) }
format.js
end
end
Rails will look for a template named bar.js and will render it and return it's content to the browser without a redirect.