Using AJAX in Rails: How do I change a button as soon as it's clicked? - ajax

Hey! I'm teaching myself Ruby, and have been stuck on this for a couple days:
I'm currently using MooTools-1.3-compat and Rails 3.
I'd like to replace one button (called "Follow") with another (called "Unfollow") as soon as someone clicks on it. I'm using :remote => true and have a file ending in .js.erb that's being called...I just need help figuring out what goes in this .js file
The "Follow" button is in a div with id="follow_form", but there are many buttons on the page, and they all have an id = "follow_form"...i.e. $("follow_form").set(...) replaces the first element and that's not correct. I need help replacing the button that made the call.
I looked at this tutorial, but the line below doesn't work for me. Could it be because I'm using MooTools instead of Prototype?
$("follow_form").update("<%= escape_javascript(render('users/unfollow')) %>")
ps. This is what I have so far, and this works:
in app/views/shared:
<%= form_for current_user.subscriptions.build(:event => #event), :remote => true do |f| %>
<div><%= f.hidden_field :event %></div>
<div class="actions"><%= f.submit "Follow" %></div>
<% end %>
in app/views/events/create.js.erb
alert("follow!"); //Temporary...this is what I'm trying to replace
*in app/controllers/subscriptions_controller.rb*
def create
#subscription = current_user.subscriptions.build(params[:subscription])
#subscription.save
respond_to do |format|
format.html { redirect_to(..) }
format.js {render :layout}
end
Any help would be greatly, greatly appreciated!

The ID selector should be used if there is only one of those elements on the page, as the Id selector us for unique IDs. A Class selector is what your looking for. Classes are used for multiple elements that have the same styles or same javascript events.
The code to replace Follow with Unfollow would be as follows in Mootools. I am not totally understanding your question, but you will need to assign a ID to the button.
document.addEvent('domready',function(){
$$('follow_form').addEvent('click',function(e){
//Ajax Here
this.set('value','Unfollow');
});
});
Mootools has a different set of functions than Prototype, so most (if not all) code designed for prototype will not work in Mootools. If you want to see the functions in Mootools, I recommend the Docs (its a good read): http://mootools.net/docs/
This code would work as well for you, if you do not/can not add unique IDs to each form element:
$$('input').addEvent('click',function(){
if(this.value == 'Follow') {
//Ajax Here
this.set('value','Unfollow');
}
});

Related

RAILS 4: Trying to get the current page to pass to render in an AJAX request

I have a fairly straight forward question.
On my site, within the header is a "invite a colleague" link to a modal that contains a form:
<%= simple_form_for(current_user, :url => send_invite_user_path(current_user), remote: true) do |f| %>
<%= f.label :email %><br>
<%= f.text_field :email, class: 'form-control' %>
<%= f.submit "Send", class: "btn btn-primary ShareSend" %>
<% end %>
Here is the send invite controller
def send_invite
#current_page = URI(request.referer).path
#email = params[:user][:email]
InviteWorker.perform_async(current_user.id, #email)
respond_to do |format|
flash.now[:success] = "Invite sent"
format.html { redirect_to #current_page, :current_page => #current_page}
format.js { render #current_page}
end
end
It works fine when not using AJAX, but I want to try to get it to work via AJAX. The issue is that the "current_page" when I open the modal and try to send via AJAX refers to the "send_invite" action and is looking for a "send_invite" template. I want it to render WHATEVER page the user is on. To add to my difficulty I am using friendly_id.....I tried using
#page_hash = Rails.application.routes.recognize_path(URI(request.referer).path)
To try to extract the user action from the current page path, but obviously this doesn't work with friendly id.
Is there a "Rails way" of capturing the current page (ignoring the modal) and passing this to render....
I hope this is clear...
An AJAX call does not trigger a complete reload of the current page (unless explicitly told to). The request is handled in the background by javascript.
In your case you should add a view called send_invite.js.erb (i guess in your app/views/users folder - assuming that send_invite belongs to UsersController) that has some javascript that notifies the user of a successful invite and closes the modal. This view could be as simple as:
alert("Invite sent!");window.closeMyInviteModal();
This script will be executed if (and each time) the AJAX call succeed.
Clean the js responder in send_invite. This will by default render the send_invite.js view.
format.js { }
See http://guides.rubyonrails.org/working_with_javascript_in_rails.html#a-simple-example

Rails remote delete and update view through Ajax

In the past, whenever I wanted to update a part of my view through Ajax, I've done the following:
create a partial out of the part I want to update and give it a unique ID, say #tracks
create a special action in the controller for that Ajax call, say remove_track that updates all the values, etc. and add format.js
create a new JS file with the same name as the action so Rails calls it automatically remove_track.js.erb which contains something like: $('#tracks').html("<%=j render 'cds/show_tracks' %>");
set remote: true in the link that calls this action.
All this is fine, but now I am trying to delete and update a common index view using the regular destroy method for flexibility, meaning I can call this method either through Ajax or normally. I figured it's such a common thing to do that there has to be a better way than all of the above.
I can get the destroy method to call my destroy.js.erb file by simply putting this into the controller:
format.js { layout: false }
and of course setting remote: true on the link.
what I cannot do is get the view to refresh. The table I want to refresh is encased in a div with a unique ID, but since it's not a partial, it refuses to refresh the content. Maybe I'm missing something.
Am I doomed to have to create a partial and refresh it with the method above or is there a more magical way of doing it (other than using Turbolinks)?
Thanks.
PS
Also, I just noticed this has the added disadvantage that I cannot pass the rest of the params to the destroy method since it only passes the object ID to destroy using the regular CRUD routes. If I try to use platform(action: destroy) or platform(method: delete) I get an error:
No route matches {:action=>"destroy", :controller=>"platforms"}
Which means I have to create a new route if I want to pass those parameters...
Yet another disadvantage to all this is that I'm repeading all the logic for searches and sorting that I have in the index method again in the destroy method. I am certain this is definitely not the way to do it.
Thanks to this page I found the proper way to do it. So simple and effective.
http://carmennorahgraydean.blogspot.com.es/2012/10/rails-328-ajax-super-basic-example.html
Update your destroy line in index.html.erb:
<%= link_to 'Destroy', pony, method: :delete, data: { confirm:
'Are you sure?' }, :remote => true, :class => 'delete_pony' %>
Create a file, destroy.js.erb, put it next to your other .erb files
(under app/views/ponies). It should look like this:
$('.delete_pony').bind('ajax:success', function() {
$(this).closest('tr').fadeOut();
});
Add format.js { render :layout => false } to your controller:
respond_to do |format|
format.html { redirect_to ponies_url }
format.json { head :no_content }
format.js { render :layout => false }
end
Hope this helps someone else.

Update a Rails 3 partial using AJAX (not a form)

I found a number of questions and answers regarding updating a partial using Ajax after submitting a form. But my question is ?simpler?, I just want to reload a partial every few seconds and load in the new data. This really can't be hard at all, and I remember doing something similar in Rails 2.3 but I can't find the answer anywhere.
Basically, I have a show.html.erb rendering a partial like so:
<div id="latest_post">
<%= render :partial=>'info/latest', :object=>#user, :as=>:user %>
</div>
The partial file located at app/views/info/_latest
<% post = user.posts.last %>
<h1>Last Post</h1>
<p><%= post.content %></p>
I just want the latest_post div to be updated every 30 seconds. Please help!
Well it turns out the answer is a little complicated:
Step 1: Use Javascript's setInterval function along with jQuery's $.get() function to load a page, say /users/1/posts/latest.
setInterval(function(){
$.get("/users/1/posts/latest.js", function(data){
$("latest_post").html(data);
},
"html")
}, 30000);
Step 2: Create a latest.js.erb and put it in your app/views/posts folder.
Contents of latest.js.erb:
<%= render partial: 'posts/latest', object: #user.posts.last, as: :post %>
Step 3: Create the above partial with whatever you'd like (if you don't need a partial, you can just write the entire contents of the div in the latest.js.erb file.)
Step 4: Add a latest method to your PostsController, that defines #user, etc. for the latest.js.erb to use.
Step 5: Add get '/latest' to your routes.rb

No route matches [POST] "/story/new" after submitting form

I've just started "Build Your Own Ruby on Rails" and I have had to use Google a lot, as the book seems to have a bunch of places where the code just doesn't work. This time, I couldn't find an answer. Okay, so here's the deal. I have a form that looks like this:
new.html.erb:
<%= form_for :story do |f| %>
<p>
name:<br />
<%= f.text_field :name %>
</p>
<p>
link: <br />
<%= f.text_field :link %>
</p>
<p>
<%= submit_tag %>
</p>
<% end %>
It shows up fine when I go to localhost:3000/story/new. The thing is, when I try to type stuff into the form and press "submit," I get this error:
Routing Error
No route matches [POST] "/story/new"
My routes.rb looks like this:
FirstApp::Application.routes.draw do
resources :story
story_controller looks like this:
def new
#story = Story.new(params[:story])
if request.post?
#story.save
end
end
The story_controller stuff for new is straight out of the book. I thought I might have had a solution here, but no dice. Any help would be greatly appreciated.
I'm guessing you meant (note the at sign):
<%= form_for #story do |f| %>
That'll probably take care of your routing issue, but as John mentions, your controller action is a bit off, too. The new action should only load a dummy model and display the new.html.erb page - the saving should take place in a separate action, called create.
Hope this helps!
Edit: Minimal controller code:
class StoriesController < ApplicationController
def new
#Make a dummy story so any default fields are filled correctly...
#story = Story.new
end
def create
#story = Story.new(params[:story])
if(#story.save)
#Saved successfully; go to the index (or wherever)...
redirect_to :action => :index
else
#Validation failed; show the "new" form again...
render :action => :new
end
end
end
First off, Rails is relies on convention over configuration when using singular vs plural names. If you want to follow convention, you have to change the line in your routes.rb to resources :stories, which would generate following routes:
stories GET /stories(.:format) stories#index
POST /stories(.:format) stories#create
new_story GET /stories/new(.:format) stories#new
edit_story GET /stories/:id/edit(.:format) stories#edit
story GET /stories/:id(.:format) stories#show
PUT /stories/:id(.:format) stories#update
DELETE /stories/:id(.:format) stories#destroy
Note, that in this case you would have to rename your controller to StoriesController. However, your routes.rb has resources :story, which generates following routes:
story_index GET /story(.:format) story#index
POST /story(.:format) story#create
new_story GET /story/new(.:format) story#new
edit_story GET /story/:id/edit(.:format) story#edit
story GET /story/:id(.:format) story#show
PUT /story/:id(.:format) story#update
DELETE /story/:id(.:format) story#destroy
As you can see, indeed, there is no route for POST /story/new. I guess, the error that you are getting is triggered by following code in your controller:
if request.post?
#story.save
end
It is quite wrong, because you trying to check for POST request inside the action that is routed to by GET. Just remove this code from your new action and add create action to your StoryController like this:
def create
#story = params[:story]
if #story.save
redirect_to #story, notice: "Story created"
else
render action: "new"
end
end
This should resolve your issue for now. But I strongly recommend using plural stories for your resources, since it will be back to haunt you again.
This is the part that you (and me) have missed from the guide:
There's one problem with this form though. If you inspect the HTML
that is generated, by viewing the source of the page, you will see
that the action attribute for the form is pointing at /articles/new.
This is a problem because this route goes to the very page that you're
on right at the moment, and that route should only be used to display
the form for a new article.
The form needs to use a different URL in order to go somewhere else.
This can be done quite simply with the :url option of form_for.
Typically in Rails, the action that is used for new form submissions
like this is called "create", and so the form should be pointed to
that action.
Edit the form_for line inside app/views/articles/new.html.erb to look like this:
<%= form_for :story, url: stories_path do |f| %>

Problem rendering an Ajax partial with link_to specifying a controller action

I have a div (id="content") on a "proposition" show page into which I want to render different partials depending on which tab at the top of the div is selected.
I'm trying to render a partial (substantive_content) with AJAX using the following link_to:
<%= link_to "Connected", :url => {:controller => "propositions",
:action => "substantive_content"}, :remote => true %>
I've got two problems:
On clicking the link, a GET request
is sent to
PropositionsController#show, and
not (I presume) to the "substantive
content" action. This just refreshes the page and doesn't do anything remotely. The correct partial isn't displayed.
I'm not sure how then to render a
partial into the "content" div.
At the moment I have this in the PropositionsController:
def substantive_content
respond_to do |format|
format.js{ render :update do |rightbar|
rightbar.replace_html 'content', 'Hello World!'
end}
end
end
... but am not sure where to go from here. Am I even going about this in the right way? I'm very new to rails, javascript, etc.
Any help with either of these problems would be fantastic. Thanks!
In your routes.rb file you should have an entry for the substantive_content action. It might look something like this:
match "substantive_content" => "propositions#substantive_content", :as => substantive_content
Then in your view the link should look something like this:
<%= link_to "Connected", substantive_content_path, :remote => true %>
I would render a js.erb file when the js request comes to the substantive_content action. The action would look soemthing like this:
def substantive_content
respond_to do |format|
format.js
end
end
Then create a substantive_content.js.erb file in your views/propositions directory. In that file you can use jquery to update the content div:
$('#content').html("<%= render :partial => 'your_partial_name_here' %>");
Without knowing more about the actual application you're working on, its hard to say if this is the best way to accomplish what you want. I generally try to stay as "RESTful" as possible with my apps so I'm reluctant to create actions aren't part of the normal index, new, create, show, etc. set. That's not to say that it isn't neccesary in this case, but it's something to think about.

Resources