Using ajax to render partial - ruby-on-rails

In my rails app I have a model Song. On one of the user's profile pages user_music_path(#user) I'm rendering all of their songs.
People can also like these songs, which is set up and working, I just want to use AJAX instead of page refreshes. The problem I'm having is when using ajax, I get the error undefined local variable or method "song" when I'm clicking the link "like".
Here is some of my code:
User's Music Page views/users/music.html.erb
...
<ul class="playlist list-group">
<%= render #songs %>
</ul>
views/songs/_song.html.erb
<li class="list-group-item">
<p class="pull-right">
<%= render :partial => 'songs/like_button', :locals => {:song => song} %>
<%= render :partial => 'songs/likes', :locals => {:song => song} %>
</p>
</li>
views/songs/_like_button.html.erb
<% if current_user.voted_on?(song) %>
<%= link_to "Unlike", unlike_song_path(song), :method => :post, :remote => true %>
<% else %>
<%= link_to "Like", like_song_path(song), :method => :post, :remote => true %>
<% end %>
views/songs/_likes.html.erb
<%= song.votes.count %>
views/songs/like.js.coffee
$("p.pull-right").html('<%= render :partial => "songs/like_button", :locals => {:song => song} %><%= render :partial => "songs/likes", :locals => {:song => song} %>');
controllers/songs_controller.rb
def like
begin
#vote = current_user.vote_for(#song = Song.find(params[:id]))
#vote.save
respond_with #song.user, :location => user_music_path(#song.user)
rescue ActiveRecord::RecordInvalid
redirect_to #song
end
end
And as mentioned earlier, the :like action worked perfectly before I starting using AJAX. I've also added respond_to :html, :js to my songs_controller. And the error I get when I try to like the song is ActionView::Template::Error (undefined local variable or method "song" for #<#<Class:0x000001028de9d0>:0x00000106ecd9f0>):

Where you have
<%= song.name %>
try instead
<%= #song.name %>
And do the same with your link_to paths and your render calls.

Do rake routes and use of of them for the link, e.g. (pseudo-code) link_to 'song', song_audio_path

Related

Determine if Rails Resource Controller has an Action Enabled

I have a Rails 3 app and am writing an "administrative" partial I'd like to include in views across many different models. The partial will include resourceful links (e.g. new, index, show), but my design fails if the resource has a route (e.g. index) disabled in routes.rb.
A sample view (views/my_resources/show.html.erb):
<% provide(:title, 'My Resource') %>
<div style="float:right;">
<%= render '/shared/resource_menu' %>
</div>
<h1>My Resource</h1>
<%= #my_resource.name %>
Excerpt from /shared/_resource_menu.html.erb:
<div class="submenu">
<b><%= File.basename(params[:controller]).pluralize.titleize %></b><br />
<% if params[:action] != 'index' %>
<%= link_to 'Index', url_for(:controller => params[:controller], :action => 'index', :only_path => true) %><br />
<% end %>
<% if params[:action] != 'new' %>
<%= link_to 'New', url_for(:controller => params[:controller], :action => 'new', :only_path => true) %><br />
<% end %>
</div>
Currently, I've hacked it so that each conditional looks like:
<% if params[:action] != 'index' && !#omit_index_in_resource_menu %>
and I set the #omit_index_in_resource_menu by hand in the relevant controllers, but that's clunkier than I would prefer.
Can I instead do something more elegant like:
<% if params[:action] != 'index' && controller.actions.include?('index') %>
Maybe this would help you:
UsersController.action_methods (it's a class method)
or
UsersController.new.available_action? :index (it's an instance method)
More info: http://api.rubyonrails.org/classes/AbstractController/Base.html#method-i-action_methods

How to change to view helper method?

I am working on a app where you can add task etc. I know this is kind of weird but I just like to see how others would implement this. How would you change the following code into a helper method and use it?
The original code
<h2>My Tasks</h2>
<% task = #work.tasks %>
<% if task.empty? %>
<%= link_to 'Create a new task', new_task_path %>
<% else %>
<%= render :partial => 'notes/note', :locals => {:note => #note} %>
<% end %>
My way of doing a helper method
def task_check
task = #work.tasks
if task.empty?
link_to 'Create a new task', new_task_path
else
render :partial => 'notes/note', :locals => {:note => #note}
end
end
In my view
<%= #work.task_check %>
Personally, I wouldn't extract this out at all, this is view logic and it belongs in the views. It definitely doesn't belong in a model, but it could arguably be extracted into a helper. I'd change it slightly:
<h2>My Tasks</h2>
<% if #work.tasks.blank? %>
<%= link_to 'Create a new task', new_task_path %>
<% else %>
<%= render :partial => 'notes/note', :locals => {:note => #note} %>
<% end %>
Calling blank? instead of empty? will work even if #work.tasks is nil
.
You can't define a helper in the model. It won't have access to render, link_to or any other controller or view methods. So just define your method almost exactly as is in a file in your helpers directory, maybe application_helpers.rb or work_helpers.rb:
def task_check(work, note)
task = work.tasks
if task.empty?
link_to 'Create a new task', new_task_path
else
render :partial => 'notes/note', :locals => {:note => note}
end
end
And then call it in your view like so:
<%= task_check(work, note) %>

rails with ajax, respond_to confusion

i have a voting system which i would like the voting response to be asynchronous.
in my 'show' view i have...
<%= render 'score' %>
which renders
<% if #user.voted?(#lesson) %>
<%= render 'voted' %>
<% else %>
<%= render 'unvoted' %>
<% end %>
and in turn renders my _voted.html.erb
#will display different images, colored or not based on selection
<% if #user.up_voted?(#lesson) %>
<div class = "up_arrow">
<%= link_to image_tag("up_color.png", alt: "Uncheck vote"),
clear_vote_lesson_url(#lesson), :method => :post, remote: true %>
</div>
<div class="average_score"><%= #lesson.votes %></div>
<div class= "down_arrow">
<%= link_to image_tag("down_uncolor.png", alt: "Vote down"),
down_vote_lesson_url(#lesson), :method => :post, remote: true %>
</div>
<!-- user down voted then -->
<% else %>
<div class = "up_arrow">
<%= link_to image_tag("up_uncolor.png", alt: "Vote up"),
up_vote_lesson_url(#lesson), :method => :post, remote: true %>
</div>
<div class="average_score"><%= #lesson.votes %></div>
<div class= "down_arrow">
<%= link_to image_tag("down_color.png", alt: "Uncheck vote"),
clear_vote_lesson_url(#lesson), :method => :post, remote: true %>
</div>
<% end %>
and my unvoted.html.erb is
<div class = "up_arrow">
<%= link_to image_tag("up_uncolor.png", alt: "Vote up"),
up_vote_lesson_url(#lesson), :method => :post, remote: true %>
</div>
<div class="average_score"><%= #lesson.votes %></div>
<div class= "down_arrow">
<%= link_to image_tag("down_uncolor.png", alt: "Vote down"),
down_vote_lesson_url(#lesson), :method => :post, remote: true %>
</div>
and then lastly in my controller i have..
def up_vote
lesson = Lesson.find(params[:id])
current_user.up_vote!(lesson)
respond_to do |format|
format.html { render :action => 'show'}
format.js { ???????? }
end
end
i have a few more actions such as down_vote but its very similar.
i guess im not really sure what to put inside my format.js block. ive tried puttings things like redirect_to lesson_path(lesson) and also render 'score' but the voting images don't get updated.
could someone please help? thanks!
UPDATE # dhoelzgen
i changed it to...
def up_vote
lesson = Lesson.find(params[:id])
current_user.up_vote!(lesson)
render :score, :layout => false
#redirect_to lesson_path(lesson)
end
i put the render :score, :layout => false in my other actions as well.
and in my view i have
<div id="surrounding_container">
<%= render 'score' %>
</div>
along with
$('.up_arrow')
.bind 'ajax:success', (event, data) ->
$('#surrounding_container').html($(data))
$('.down_arrow')
.bind 'ajax:success', (event, data) ->
$('#surrounding_container').html($(data))
in my lessons.js.coffee.
am i missing something? the images still don't change asynchronously
You have several options, I would suggest just returning the rendered score (not as js / json) and then putting it to (surrounding) div tag.
Render the score without the layout like this
render :score, :layout => false
and then use JavaScript (CoffeeScript in my example) to replace the content of the div tag
$('.up_arrow a')
.bind 'ajax:success', (event, data) ->
$('#surrounding_container').html($(data))
Alternatively, you can return the result as json and modify the page by hand, but I think the option above results in less effort.

An instance of a model to render into a partial

I render an instance of a model into a partial with the following code <%= render #places %>.
I created _places.html.erb. I thought I could use the following code since the render method is supposed to iterates trhough the places collection :
<li>
<%= link_to place.name, place %>
</li>
I get this error : undefined local variable or method 'place' for #<#<Class:0x007fc1c0a4e6b8>:0x007fc1c05050a8>
I have to use to make it work :
<% #places.each do |place| %>
<li>
<%= link_to place.name, place %>
</li>
<% end%>
try this:
<%= render :partial => 'places.html', :collection => #places, :as => :place %>
Or, if you have the partial file as singular name, you can do it like this(without the variable :as)
<%= render :partial => 'place.html', :collection => #places %>

AJAX Form Issues with Ruby on Rails

I'm sure this question has been asked before in a different context but I'm still so stuck with figuring out AJAX Rails and, I guess, Rails in general (kinda makes me wonder if I should just go back to PHP...). Well anyways I have this form that I want to AJAXify.
This is the "list" view which is part of the "subject" controller
<h1>Listing Subjects</h1>
<ul id="subject_list">
<% #subjects.each do |c| %>
<li><%= link_to c.name, :action => 'show', :id => c.id %></li>
<% end %>
</ul>
<p id="add_link"><%= link_to_function("Add a Subject",
"Element.remove('add_link'); Element.show('add_subject')")%></p>
<div id="add_subject" style="display:none;">
<%= form_tag(:action => 'create') do%>
Name: <%= text_field "subject", "name" %>
<%= submit_tag 'Add' %>
<% end %>
</div>
Code for my "subject" controller
class SubjectController < ApplicationController
def list
#subjects = User.find(:all)
end
def show
#subject = User.find(params[:id])
end
def create
#subject = User.new(params[:subject])
if #subject.save
render :partial => 'subject', :object => #subject
end
end
end
My "subject" partial
<li id="subject_<%= subject.id %>">
<%= link_to subject.name, :action => 'show', :id => subject.id %>
</li>
And the User is just a simple model I made that contains two columns "name" and "email".
How this code currently works is that when you click "Add", the textfield input is revealed. When you type something in the input and submit it, the "_show" partial is rendered in the create link. I was following a Rails 2.0 tutorial but I have 3.0 and I've read through some tutorials and they all mention ":remote => true" and jquery_ujs.js but I have no idea how to apply them to a "form_tag" rather than "form_for" Rails helper.
Basically I want to asynchronously add the element to the bottom of the list without a page load. I've really tried to understand absolutely all of the tutorials I could find but I just can't figure it out.
I believe that you'd better use some Unobtrusive JavaScript to tell to your app
and browser what exactly you want to render and how.
You want too much from simple render :partial => 'subject', :object => #subject line of code.
Here's my snippet that may be helpful to you.
# in the view (:remote => true for form_tag is not problem at all)
<%= form_tag({:controller => :products, :action => :empty_cart }, {:id => 'empty_cart', :remote => true}) do %>
<%= submit_tag 'Clear' %>
<% end %>
# in the controller (note that format.js section in the respond_to block)
def empty_cart
...
respond_to do |format|
format.html { redirect_to :root, :notice => 'Your cart is empty now' } # in the case of disabled JS support
format.js { render :js => "$('#empty_cart').fadeOut()" } # or you can place js code in the empty_cart.js.erb file and specify format.js here without the block
end
end
Check this article if I'm not clear enough.

Resources