I'm wanting to add some AJAX functionality to my Rails app, but have no idea where to start.
Here is the method that adds an item to an order:
def add_item_to_order
if session[:order_id].nil?
#order = Order.new #Don't create an order until there is an item to be added to it.
#order.account_id = session[:user_id]
else
#order = Order.find(session[:order_id])
end
item = Item.find(params[:id])
o_item = OrderItem.new
o_item.item_id = item.id
#order.order_items << o_item
#order.total += item.sale_price
#order.save
session[:order_id] = #order.id
redirect_to order_home_path
end
This is run when the user clicks:
<%= link_to item.name, add_item_to_order_path(item.id), :class => "fixed medium green button"%>
Can anyone give me any tips on how to get started, so the the item is added to the order via AJAX?
Check on how to render javascript. In normal requests one would redirect to some action or render some view etc, for a XHR (XmlHttpRequest) you can render javascript through a server-sided js template that would be rendered. You will have to use the LegacyPrototypeHelpers provided for Rails-3 as the original helpers were only officially available for Rails-2.
A better approach(unobtrusive as Rails 3 prefers) will be to just send some data from the server. In the following example you have above I guess if you send item.id via a JSON object or some other format and then read it in the success callback of the place from where you made the XMLHttpRequest, then after getting the item.id you could create the HTML that the link_to creates and then append it to the DOM.
Great tutorial, did this myself: http://ruby.railstutorial.org/ruby-on-rails-tutorial-book Chapter 12 has some stuff on Ajax.
Important part is to set your link_to paramater data-remote to true:
<%= link_to item.name, add_item_to_order_path(item.id),
:class => "fixed medium green button" data-remote="true" method="post"%>
and in your controller you add
def add_item_to_order
# other stuff
# at the bottom:
respond_to do |format|
format.html { redirect_to order_home_path }
format.js
end
end
Then you'll need a .js.erb file to handle the format.js repsonse:
$("your_form").update("<%= escape_javascript(render('partial_page')) %>")
and a partial page file to hold the new data..
Related
I'm working on an order system, I passed some information about a new order from controller (orders#confirm) to a confirmation page using instance variable (such as #detail). On the confirmation page, users are supposed to confirm the information and submit a form to create the new order (orders#create). If the post action fails, I want it to stay on the same confirmation page and preserve all the information on the page for the user:
def create
#order = Order.new(order_params)
if verify_recaptcha(model: #order) && #order.save
redirect_to items_url
else
render :confirm
end
end
The code is not working because all the variables that I passed from orders#confirm to the confirmation page are lost. I know I can recreate them, but is there any better ways to preserve those information? Thank you very much!
Within your approach, you have to rebuild the #detail object in the current action create in order to make your confirm.html.erb view to be rendered properly.
It is possible. However, I think there is a better way that you can let the user confirms the order by AJAX ( which is dead simple with Rails ) so the user can stay on the page if the confirmation failed.
In your confirm.html.erb, suppose you have a form to let user confirm, just change it to remote: true
<%= form_for #order, remote: true, format: :js do |form| %>
<%# blah blah %>
<% end %>
Modify your controller
def create
#order = Order.new(order_params)
if verify_recaptcha(model: #order) && #order.save
# Redirect when success
render js: "window.location = '#{your_desired_path}'"
else
# Display error to user.
render "ajax_create_error", status: :bad_request
end
end
Now you can create a file name ajax_create_error.js.erb to display error to the user
# app/views/your_controller_name/ajax_create_error.js.erb
alert("Cannot create order");
Using rails and .js.erb to make an AJAX request (and append values to a div), how do you prevent rendering a new layout? In other words, stay on the same page without going anywhere and just append the fresh data from the server in a div. No reloading the same page, no redirecting.
At the moment my controller looks like this
def update_shipping
#order = Order.find(params[:id])
#order.shipping_option_id = params[:shipping_options]
#order.save!
respond_to do |format|
format.js
format.html
end
end
and my form like zisss:
<%= form_tag update_shipping_order_path(#order), method: :put, remote: true do %>
<%= select_tag 'shipping_options', #options_for_select, onchange: 'this.form.submit()' %>
<% end %>
and my routes look like a so:
resources :orders do
member do
put :update_shipping
end
end
But I get a 'Template is Missing' error
Please help!!
You need to add a update_shipping.js.erb file under app/views/your_controller/ directory. Note the name of the javascript file should be same as the action. Since you have a remote:true in your form so rails will try to render a javascript template in your case update_shipping.js.erb.
Now in your update_shipping.js.erb file write some basic javascript to update the page elements like
#update_shipping.js.erb
$('.some-div').html(<%=j #model.some_value' %>)
Try this:-
respond_to do |format|
format.js { render :nothing => true }
format.html
end
If you don't want to render a layout, you can use !request.xhr? like so:
respond_to do |format|
format.html { layout: !request.xhr? }
format.js
end
If you're looking to get your ajax-powered JS to fire, you just need to call your .js.erb file the same as your view:
#app/views/controller/update_shipping.js.erb
alert("This JS is returned & fired after the Ajax request");
You'll be best doing this in your routes.rb too:
resources :orders do
put :update_shipping
end
A little late, I came across this searching for the same issue. It must of slipped out of my mind at some point while working with action cable, but what is needed is a http response code of no_content. Http response codes tell the browser how to act when a request is returned. Here is a link to a list of them, and their symbols in rails. More on 204 no content
Here is how it would look:
def update_shipping
#order = Order.find(params[:id])
#order.shipping_option_id = params[:shipping_options]
#order.save!
head :no_content #or head 204
end
edit: what solved the solution for me was a link provided by William Denniss in this stack overflow question
I have already tried reading numerous articles and posts on the subject, but I am still bewildered and can't figure out what I need to do. Can someone explain exactly what I need to do?
I have a Rails server (3.2.11), with a model Game that represents the current state of a Ra game. My games_controller has the methods show for displaying the game and doturn for processing someone's action. The game view shows the current state of the game and contains links for each possible move, which submits back to the doturn action. My goal is to get the view to automatically refresh after a set time period or whenever one of the links is clicked, via AJAX. But try as I might, I can't get either one to work.
My views are currently setup as follows in the /views/games folder.
_game.html.erb actually renders the game
show.html.erb just adds a header and then renders the partial, _game.
show.js.erb is supposed to refresh the page. I'm not really sure how this works
I have also enabled Jquery with the //= require etc lines (they were already there by default)
Edit: I got AJAX on the links working, but I still have no idea how to make it autorefresh after a certain amount of time. Any ideas?
show.html.erb:
<h1>Game <%= #game._id %></h1>
<div id="test"><%= render #game %></div>
show.js.erb:
$('#test').html('<%= escape_javascript (render #game) %>')
Game Controller
# GET /games/1
# GET /games/1.json
def show
#game = Game.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #game }
format.js
end
end
# POST /games
def doturn
error = Proc.new do |message|
flash[:alert] = message
return render :inline => "<%= flash[:alert] %>"
#return redirect_to :back #must return from method immediately
end
#game = Game.where(id: params[:id]).first
if #game.nil?
error.call "Game not found"
end
#all the game update logic here
#game.save
redirect_to :action => "show", :id => #game._id
end
Here's a simplified version of my partial view. I cut out a lot of the rendering stuff, but it still has all the important behaviors. I figure I should try to get this view working first, and then I can add back in the rest of the code later.
<% game.game_players.each_with_index do |player, i| %>
<table>
<tr><td>Player <%= i+1 %></td></tr>
<tr><td>Suns: </td><td><%= player.suns %></td></tr>
<tr><td>Pharaohs: </td><td><%= player.pharaohs %></td></tr>
</table><p>
<%
make_linkt = lambda do |text, kwargs|
concat link_to text, {:remote => true, :method => "post", :action => "doturn", :id => game, :player => i}.merge(kwargs) # ** requires ruby 2
concat raw "<br/>"
end
if i == game.turn
if game.god_status == 0
make_linkt.call("Invoke Ra", :type=>"invoke")
if game.auction_track.size < 8
make_linkt.call("Draw tile", :type=>"draw")
end
else
make_linkt.call("Done", :type=>"god", :vals=>[], :endturn=>1)
end
end
%>
<% end %>
<br />
The project is over, but I figure I should document what I discovered in case anyone else comes across this question in the future. It's been a while, so I don't remember exactly what I did, but here's the best I can recall.
Setting :remote => true in the link_to parameters will mark the link with a special tag that causes it to automatically be turned into an AJAX link by Rails unobtrusive JS. Since it's an ordinary html link until the javascript runs, no extra effort is required to make the page work without Javascript enabled.
In order to get these AJAX links to work, you need two things. First, you have to define a js.erb file. In my case I had this in show.js.erb.
$('#test').html('<%= escape_javascript (render #game) %>')
Note that render #game is a shorthand that will render the partial view _game.html.erb with the #game parameter exposed as game.
Second, you need your controller to respond to the js format. Since my view was being rendered through the show action, I needed to add a respond for show.js. I put the following in my games_controller.
def show
#game = Game.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #game }
format.js
end
end
Getting the autorefresh is more complicated, because Rails doesn't provide magic to do it for you. I ended up creating a recurring function in the javascript to manually refresh the page with AJAX.
I put the following in my view, show.html.erb
<script>
$(document).ready(function() {
setInterval(function() {
$.ajax({
type: 'GET',
url: '<%= url_for(:action => 'show_partial', :id => #game) %>',
data: {},
cache: false,
success: function(result) {
$('#test').html(result)
}
});
}, 2000);
});
</script>
I then added an a show_partial action to the controller so the js could call it.
def show_partial
#game = Game.find(params[:id])
return render #game
end
You could execute a javascript setTimeout method that will be requesting the show.js view.
In your view (or in a .js asset rendered only for your specific page that shows the game):
<script type="text/javascript>
function updateGame(data){
$.ajax({
type: "GET",
url: "/games/whatever-your-path-is.js",
dataType: "script"
});
}
setTimeout(updateGame({gameId: '<%= #game.id %>'}, 5000)
</script>
The data variable in the updateGame, will be available in the ajax call in order for example to "generate" the appropriate url, ex: the line with url could be:
url: "/games/" + data.gameId + "/whatever.js"
I recently started with Rails 3.2 and am stuck while trying to implement some ajax functionality in my app. I followed this railscast completely (http://railscasts.com/episodes/240-search-sort-paginate-with-ajax). Beyond this I want to implement a shortlist button for each product which lets user shortlist products and store them in the session. I also want a small list of shortlisted products to show up on the same page, which needs to be ajax updated.
I am wondering what is the best way to do that. I currently implemented the link_to buttons with remote tag and a helper function to change the link to shortlist/unshortlist. I also, used a conditional div to show the shortlist based on the length of shortlist. However, the issue is that whenever I shortlist, the order of the products table is also reset.
Here are snippets of my code :-
Application_helper.rb
def shortlist_unshortlist_link(product_id )
if (user_session.already_shortlisted? product_id )
link_to 'Unshortlist', { action: 'unshortlist', id: product_id }, remote => 'true'
else
link_to 'Shortlist', { action: 'shortlist', id: product_id }, remote => 'true'
end
end
def shortlist_div
shortlist=user_session.shortlist
if (user_session.shortlist.length > 0)
render :partial => 'pages/shortlist_fields'
end
end
products/index.html.erb
<div id="product">
<% #products.each do |product| %>
<tr>....
<td><%= shortlist_unshortlist_link(product.id.to_s) %></td>
</table>
</div>
<div class="shortlist"><%= shortlist_div() %> </div>
products_controller.rb
def shortlist
user_session.add_to_shortlist(params[:id])
redirect_to products_path
end
def unshortlist
user_session.remove_from_shortlist(params[:id])
redirect_to products_path
end
I think, the issue is because of redirect_to, but I am not getting how to avoid this without hurting the functionality. Am I on a totally wrong path here. Is this a good way to implement this. Any suggestions.
thanks,
Amit
you should use in shortlist method,
respond_to do |format|
format.html {redirect_to products_path }#shortlist.html.erb
format.js #shortlist.js.erb
end
and write your java script to #shortlist.js.erb file.
Do the same with unshortlist.
I agree with Sush, you didn't response to the ajax request from link_to.
In your controller, shortlist method, response to the ajax request
respond_to do |format|
format.html {redirect_to products_path }
format.js
end
By convention in Rails, format.js will execute the js.erb file with the same name as your method.
And in the shortlist.js.erb, you may write something like:
$('#shortlist').html('<%= escape_javascript(render "pages/shortlist_fields")%>');
Besides, you can also call the same js.erb file.
In the unshortlist method, you can do it like that:
respond_to do |format|
format.html {redirect_to products_path }
format.js {render :action=>'shortlist'}
end
i had a index page with url as www.exa,com/users/jude.In that page i want to submit a from which appears from popup dialog.The action of the popup is going to send_message,Whenever i submits, via ajax the data gets stored but not the page gets reflected.
My controller
def send_message
#message = current_user.messages.build(params[:message])
#message.receiver_id = #user.id
respond_to do |format|
if #message.save
format.html {redirect_to user_url(current_user)}
format.js
end
end
end
View file
%div.jqmWindow#dialog
%div.dialog_content
-form_for Message.new,:url=>'users_message_path',:remote=>true do |f|
=hidden_field_tag :id,#user.login
=f.text_area 'content',:rows => 10, :cols => 25
=f.submit 'Send'
My js.erb
$('#dialog').html("hello")
Problem is ajax is requestuing the action /users/send_message and not /users/jude
Whenever we use haml as a markup, then we have all the files should have .html.haml naming conventions, since views with the .html.haml extension will automatically use Haml.
Now in format.js file we can our updated or inserted data to show
$('#dialog').html('<%= escape_javascript "You have successfully sent your message to #{#message.receiver_id.login}"%>')
Now the data gets inserted via ajax calls