Respond_to many AJAX calls from same controller - ajax

I have a page which has 2 or more lists which I want to paginate (with kaminari for instance)
#list_a and #list_b
these are being created in my pages#home controller
I want to have a link for each list (with remote: true) to append new elements via javascript.
That would make
respond_to do |format|
format.js
..
end
will fire a home.js.erb call.
but in home.js.erb I don't know how to know which link made the home.js.erb fire.
How do I differentiate between the 2 ajax links - from the controller or in the js? and how?

try this,
in view file
<%= link_to "link_1", <your path>, :list => 1, remote: true %>
<%= link_to "link_2", <your path>, :list => 2, remote: true %>
in home.js.erb
<% if params[:list] == "1" %>
// link 1 clicked
<% elsif params[:list] == "2" %>
// link 2 clicked
<% end %>

Related

Rails 4: Routing and sharing actions between controllers? What's the proper convention?

I'm building a Todo list in rails trying to learn how it all works. I'm having an issue with calling the action of a controller from the view of another controller.
I have a TodoList has_many TodoItem and TodoItem belongs_to TodoList.
TodoList#Model
has_many :todo_items
def has_completed_items?
todo_items.complete.size > 0
end
def has_incompleted_items?
todo_items.incomplete.size > 0
end
TodoItem#Model
belongs_to :todo_list
scope :complete, -> { where("completed_at is not null") }
scope :incomplete, -> { where(completed_at: nil) }
def completed?
!completed_at.blank?
end
Routes
resources :todo_lists do
resources :todo_items do
member do
patch :complete
end
end
end
Patch generates this route:
complete_todo_list_todo_item PATCH
/todo_lists/:todo_list_id/todo_items/:id/complete(.:format)
todo_items#complete
Now I can call this path from my TodoItem views without a problem like this: <%= link_to "Mark", complete_todo_list_todo_item_path(todo_item), method: :patch %>
After adding complete/incomplete scope to my TodoItem I added the following to my TodoList show view:
TodoList#Show
<% #todo_list.has_incompleted_items? %>
<% #todo_list.todo_items.incomplete.each do |item| %>
<li><%= item.content %></li>
<% end %>
This is properly displaying the incomplete items, so I tried to put a link to mark the item complete from within this view like this:
<% #todo_list.has_incompleted_items? %>
<% #todo_list.todo_items.incomplete.each do |item| %>
<li><%= item.content %>
<%= link_to "Mark", complete_todo_list_todo_item_path(item), method: :patch %></li>
<% end %>
Now this is where I get the following error:
NoMethodError in TodoLists#show undefined method
`complete_todo_list_todo_items_path' for #<#:0x000001071cf540>
Why is this complete#action undefined from my TodoList#show view?
What is the proper convention to share actions between controllers?
Thank you for your help.
I was able to solve this problem by removing the member block from the routes file:
resources :todo_lists do
resources :todo_items do
patch :complete
end
end
Now my action route was updated to: todo_list_todo_item_complete
So from my view I had to call the new route with it's nested resource:
link_to "Mark", todo_list_todo_item_complete_path(#todo_list, item), method: :patch
Lastly I had to update my controller to locate the proper todo_item#id in the complete block:
#todo_item = #todo_list.todo_items.find(params[:todo_item_id])

Rails 3 Ajax not working

I'm new to rails and ajax so I'm experimenting by building a simple app. I created a simple to do app with User and Tasks (user has many tasks). Using Devise to handle the authentication. I set up the task CRUD and implemented best_in_place (in place editing gem) and got it to work. What I wanted to do next is add ajax create and delete following some example resources such as creating a 100% ajax CRUD. After setting up all the necessary code for the create and destroy in tasks_controller, I'm getting DELETE http://localhost:3000/tasks/26 500 (Internal Server Error) as seen in the firebug console log. These are the errors printed in the console in firebug:
DELETE http://localhost:3000/tasks/26 500 (Internal Server Error)
jQuery.ajaxTransport.send
jQuery.extend.ajax
$.rails.rails.ajax
$.rails.rails.handleRemote
(anonymous function)
jQuery.event.dispatch
jQuery.event.add.elemData.handle.eventHandle
when the destroy link is clicked and same with create. The task got deleted and created after refresh but the ajax is just not working. I spent some time trying to figure out what's wrong but don't have any luck yet so I'm hoping I can get some help here while I keep digging into it. Below are my setups:
First the controller code:
def destroy
#task = current_user.tasks.find(params[:id])
#task.destroy
flash[:notice] = "Successfully destroyed post."
respond_to do |format|
format.html { }
format.js { }
end
end
def create
#task = current_user.tasks.build(params[:task])
respond_with(#task) do |format|
if #task.save
flash[:notice] = "Task was created successfully!"
respond_to do |format|
format.html { redirect_to tasks_url }
format.xml { render :xml => #task }
format.json { render :json => #task}
format.js
end
else
format.html { render :action => :new }
format.js
end
end
end
The task list partial to display the task list and delete
<table>
<% #tasks.each do |task| %>
<tr>
<td><%= best_in_place task, :detail %></td>
<!-- <p><%= best_in_place task, :completed, type: :checkbox %></p> -->
<td><%= link_to "Destroy", task, :confirm => 'Are you sure?', :method => :delete, :remote => true, :id=>'delete' %></td>
</tr>
<% end %>
</table>
The create form (without the other view stuff):
true) do |f| %>
<%= f.label :detail %>
<%= f.text_field :detail %>
<%= f.submit %> <% end %>
This is the javascript tag for application.html.erb (include jquery.js, jquery.ujs, application.js and other not so related js files):
Lastly I'm just trying to call an alert inside destroy.js.erb and with create.js.erb:
alert("Task was deleted");
Again, the crud is working normally when refreshed, it feels like the destroy.js.erb and create.js.erb is not linked somehow. I appreciated if you can provide a few hints.
Here is the error from the console after hitting delete:
ActionView::Template::Error (You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.each):
1:
2: <table>
3: <% #tasks.each do |task| %>
4:
5: <tr>
6: <td><%= best_in_place task, :detail %></td>
app/views/tasks/_tasks.html.erb:3:in `_app_views_tasks__tasks_html_erb___1747031968745406293_70257861453000'
app/views/tasks/destroy.js.erb:4:in `_app_views_tasks_destroy_js_erb___3673072470672404292_70257861472560'
It looks as though you have a syntax error in the destroy action. I think you're missing an end for the respond_to block.

Rails 3.1 Ajax forms don't show validation

I'm using updated pre Ruby on Rails on Ruby 1.9.2 (with rvm), and made a new test application with
$ rails generate scaffold Project name:string
and
class Project < ActiveRecord::Base
validates_presence_of :name
end
I change
<%= form_for #project do |f| %>
to
<%= form_for #project, :remote => true do |f| %>
I can now still (without any changes in the controller) add new items to the project. If I try to add with empty in the name field, it will not add anything (validates_presence_of :name stops this), but I don't get any validation error messages. I have tried the same on an application converted from 3.0 I'm working on with same results. Here I had:
class KursController < ApplicationController
# GET /kurs
# GET /kurs.xml
respond_to :js, :html
and:
def update
#kur = Kur.find(params[:id])
#kur.update_attributes(params[:kur])
flash[:notice] = "Lagret" if #kur.save
respond_with( #kur, :layout => !request.xhr? )
end
In 3.1 I get no validation error messages. Is this because of a bug in Ruby on Rails 3.1 or something I should do different?
You are missing to call these errors. Do something like this:
<%= form_for #project do |f| %>
<%= render :partial => 'my_error_messages_for', :locals => {:collection => #project} %>
In "_my_error_messages_for.html.erb":
<% if collection.errors.any? %>
<ul>
<% collection.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
You can create a helper for this work.
I don't believe that Ruby on Rails will automatically include the partial that you've requested. You'll need to add some JavaScript code to get it to work correctly. Something like this I imagine:
// Application.js
$("form").bind("ajax:success", function (event, data, status, xhr) {
$(event.target).replaceWith(data);
});

Rails 3 - jQuery returned from AJAX call is not being evaluated

I have a Rails 3 form with :remote => true, like this:
<%= form_for :line_item, :url => line_items_path(:product_id => product), :remote => true do |f| %>
<%= f.submit "Add to Cart" %>
<% end -%>
Then in my view file, create.js.erb, I have some jQuery code:
$('#cart').html('test')
The Net tab in Firebug shows that this code does get returned, but it doesn't do anything. As if it's not getting evaluated. I have the jquery-rails gem installed. What am I missing?
You mast rename the file to create.rjs and to write in file
page << "$('#cart').html('test')"

Rails3 routing error with ajax

UPDATED CODE at the bottom
I am creating a story voting app via Simply Rails 2 book. I am getting this error when I click the button to vote up a story:
No route matches "/stories/4-pure-css-icons-showcase"
My routing file looks like this:
Shovell::Application.routes.draw do
get "votes/create"
root :to => "stories#index"
resources :stories do
resources :votes
end
end
votes_controller.rb:
class VotesController < ApplicationController
def create
#story = Story.find(params[:story_id])
#story.votes.create
end
end
create.rsj :
page.replace_html 'vote_score', "Score: #{#story.votes.size}"
page[:vote_score].visual_effect :highlight
show.html.erb:
<h2>
<span id="vote_score">
Score: <%= #story.votes.size %>
</span>
<%= #story.name %>
</h2>
<p>
<%= link_to #story.link, #story.link %>
</p>
<div id="vote_form">
<%= form_tag :url => story_votes_path(#story), :remote => true do %>
<%= submit_tag 'shove it' %>
<% end %>
</div>
story.rb :
class Story < ActiveRecord::Base
validates_presence_of :name, :link
has_many :votes
def to_param
"#{id}-#{name.gsub(/\W/, '-').downcase}"
end
end
I've been working through a number of other errors before this having to do with deprecated code and so forth, so I feel somewhat lost at the moment. It seems like it should just be a routing a issue, but since I've been working through AJAX errors that also have to do with the vote function I wanted to post those files just in case it was more than routing.
It says no route matches "/stories/4-pure-css-icons-showcase" but when I visit "/stories" (my root) and click on the link to take me to "/stories/4-pure-css-icons-showcase" it works fine, however after clicking on the vote button I get this error. As you could probably tell after reading the code, it is suppose to update the vote count and do a :highlight via ajax.
UPDATE:
Changed code (all changes are per Sam's advice):
routes:
Shovell::Application.routes.draw do
resources :votes
root :to => "stories#index"
resources :stories do
resources :votes
end
show.html.erb:
<div id="vote_form">
<%= form_tag :url => new_story_vote_path(#story), :remote => true do %>
<%= submit_tag 'shove it' %>
<% end %>
</div>
votes_controller.rb
class VotesController < ApplicationController
def create
#story = Story.find(params[:story_id])
#story.votes.create
respond_to do |format|
format.html
format.js
end
end
The problem is still exactly the same, but I think (read: hope) we are making progress!
The scenario: My index (/stories) page randomly displays a story from the database, when you click the link it takes you to the story's internal page (ex. /stories/2-sitepoint-forums) on this page it displays the number of votes the story has and has a button to vote for it. When you click the vote button it is suppose to use ajax to update the #story.vote.size and use a :highlight visual effect. However, the problem is that when you click the vote button the page changes to a "Routing Error" page which displays:
No route matches "/stories/2-sitepoint-forums"
Its weird to me because you can in fact be routed to that address and you are from the link on the first page...
Here is the error in the console:
Started POST "/stories/2-sitepoint-forums?url=%2Fstories%2F2-sitepoint-forums%2F
votes%2F2-sitepoint-forums&remote=true" for 127.0.0.1 at 2010-11-08 16:30:17 -08
00
ActionController::RoutingError (No route matches "/stories/2-sitepoint-forums"):
Rendered C:/Ruby192/lib/ruby/gems/1.9.1/gems/actionpack-3.0.0.rc2/lib/action_dis
patch/middleware/templates/rescues/routing_error.erb within rescues/layout (1.0m
s)
Im not sure if this is any more telling, but I thought I'd add it incase.
New:
I have not been able to solve this problem as of yet. Because I still don't feel like I completely understand the issue I have decided to move over to the Ruby on Rails 3 Tutorial Book online and see if I can't figure it out while working through it. Since I was planning to do it next anyway (I have plans to combine both apps later) it appears now is the time.
<%= form_tag :url => new_story_vote_path(#story), :remote => true do %>
<%= submit_tag 'shove it' %>
<% end %>
That should send it to the create action.
def create
#story = Story.find(params[:story_id])
#story.votes.create
respond_to do |format|
format.html
format.js
end
end
And that should take care of your ajax.
take this get "votes/create" out of your routes
and add this
map.resources :votes
I'm not familiar with using form_tag and :remote (as I normally just write jQuery for stuff like this) but a couple of things definitely pop out at me with what you're doing here that may help you resolve the issue.
First of all, I think you can rework the way you set up a vote and thus the way you set up the form for the vote. In the stories controller, for the show action, I'd set up the vote right away:
#vote = Vote.new(:story_id => #story.id)
This lets you set up your form as so:
= form_for(#vote), :remote => true do |f|
= hidden_field f.story_id
= submit_tag "Vote"
This is both a cleaner way of doing things, in my opinion, but also may fix the general issue you are dealing with, because you are now passing data with the form (the hidden field) in your POST request. Rails will behave unexpectedly if you perform AJAX POST requests that do not actually submit data.
In other words, your original form is likely running as an AJAX POST request but it would have worked better as an AJAX GET request, since it is not actually submitting data, it is simply "hitting" an URL.
I am not sure if you found the answer to your problem yet, but I wanted to post for others that may be looking for an answer similar to yours.
The code:
<%= form_tag :url => new_story_vote_path(#story), :remote => true do %>
<%= submit_tag 'shove it' %>
<% end %>
will result in /stories/:story_id/votes/new url with a :post request. It won't work because the new route is a :get method request. If you wanted to go to the new method, you'll need to tell the form to use the get http method.
<%= form_tag :url => new_story_vote_path(#story), :remote => true, :html => { :method => :get } do %>
<%= submit_tag 'shove it' %>
<% end %>
However, I think that you are wanting to route to the create method in your controller. I would do something like:
<%= form_tag :url => story_votes_path(#story), :remote => true do %>
<%= submit_tag 'shove it' %>
<% end %>
This should route correctly to the create method in your VotesController.

Resources