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"
Related
I have to ask about something that probably no one uses anymore. I want to display flash[:notice] after successfully AJAX action. I'm aware of this and that one and even this gist but none of them fit my example:
#controller code
def new
#registrant = Registrant.new
respond_to do |format|
format.html
if params[:add_patient_to_caregiver]
format.js { render partial: 'add_patient_to_caregiver' }
end
end
end
#view triggered controller#new action via AJAX
<%= link_to 'Add Patient to Caregiver', patient_to_caregiver_path(add_patient_to_caregiver: true, patient_to_caregiver: registrant.id), method: :get, remote: true %>
I want to have something like format.js { render partial: 'add_patient_to_caregiver', flash[:notice] = 'Patient Added' } to display it in a view. I've come up with a workaround:
_add_patient_to_caregiver.js.erb
$("#add-patient").html("<%= escape_javascript(render :partial => 'registrants/add_patient') %>");
$("#flash-messages").after("<div class='alert alert-success'> Patient Added </div>");
And flash message shows up but there are no close button there. Is there any better way to do so? or how to add close button to that message so that the whole page doesn't reload when it is pressed?
I have this incredible simple form:
<%= form_tag("/portal/search", method: 'get', remote: true ) do %>
<%= label_tag(:query, 'Search for:') %>
<%= text_field_tag(:query) %>
<%= submit_tag("Find") %>
<% end %>
<div id="results"></div>
I know from the documentation, I can use AJAX through the remote option:
<%= form_tag("/portal/search", method: 'get', remote: true ) do %>
Then I get stuck. I know how to generate results in a view/partial, but how do I get that partial inside the div element? I know a bit of JQuery, so I can select the element. I found a lot of results, but they seem to miss that lightbulb moment for me. I am using Rails 4.1.6
My results are just simple records from a model, like Book (title, author)
Thank you for helping!
EDIT
I've won the cup for missing the point by a long shot. I had to add the search.js.erb inside the view folder instead of assets.
Let's say you get #results out of your search method.
In the controller, search method:
respond_to do |format|
format.html {render or redirect_to wherever you need it}
format.js
end
Then all you need is a file named search.js.erb to run the required js at the end of your request. It might look something like:
$('#results_div').html("<%= j #results.pluck(:title).join('<br/>') %>")
When you add remote: true jquery-ujs will provide you the ajax request (by default this javascript lib is required in app/assets/javascripts/application.js).
The ajax call will request a 'text/javascript' response. for that reason your server code should reply with:
# action
def search_query
respond_to do |format|
format.js {
# additional code
}
end
end
If in your view (search_query.js.erb) you provide javascript, it will be executed. That is why everyone is replying you with a $('#my_div_id').html('my html text') suggestion, which when executed will replace your div content with the new HTML.
If for some reason you want to return a json data structure, then you should provide a different data-type:
form_tag("/jquery_ujs/search_query", remote: true, 'data-type' => :json) do
# ....
end
And you should reply with:
# action
def search_query
respond_to do |format|
format.json { render json: #my_object }
end
end
And handle the success event:
<script>
$(document).ready(function(){
$("form#my_form").on('ajax:success', function(event, data, status, xhr) {
console.log("Great!");
// use data to access to your json data structure
});
$("form#my_form").on('ajax:error', function(event, xhr, status, error) {
console.log("sorry mate!");
});
// ....
})
</script>
You can also request a html response (in case you want to return a table, for instance), with :'data-type' => :html and format.html { render layout: false }
Ajax
Firstly, the Rails UJS (unobtrusive javascript) driver just gives you a "canned" way to send an ajax request to your browser. To avoid confusion, it's best to appreciate that you will be sending Ajax requests regardless of whether you use Rails UJS or the standard Ajax method
This means that the process of capturing the response from your Ajax is still the same - you need to make sure you have to catch the response from the system
Either :-
#app/assets/javascripts/application.js
$(document).on("ajax:success", ".element", function(status, data, xhr) {
// do something here
});
or
#app/controllers/portal_controller.rb
class PortalController < ApplicationController
def search
respond_to do |format|
format.js #-> app/views/portal/search.js.erb
format.html
end
end
end
#app/views/portal/search.js.erb
//something here
Fix
how do I get that partial inside the div element
You'll be able to use JS:
#app/controllers/portal_controller.rb
class PortalController < ApplicationController
def search
#results = ...
respond_to do |format|
format.js
format.html
end
end
end
#app/views/portal/search.js.erb
$(".div").html("<%=j render #results %>");
I have got ajax comment submit and delete, submit works perfectly but delete is not working.
Below is my code :-
Controller
def destroy
#status_update = StatusUpdate.find(params[:id])
if #user == current_user
if #status_update.destroy
respond_with do |format|
format.html do
if request.xhr?
render :partial => "users/post_status_update", :locals => {:user => #user}, :layout => false, :status => :created
else
redirect_to #user
end
end
end
end
end
end
Javascript
$(document).ready(function(){
$('.delete-status-update')
.on("ajax:beforeSend", function(evt, xhr, settings){
})
.on("ajax:success", function(evt, data, status, xhr){
$('.status-partial').replaceWith(xhr.responseText);
})
});
View
.status-partial
-#user.status_updates.latest.limit(5).each do |status_update|
.row-fluid.cycle-status-update{:class => cycle("dark", "light")}
.row-fluid
.span11
= status_update.status
.span1
.delete-status-update-link
%strong= link_to "X", [#user,status_update], :remote => true, :method => :delete, :class => "delete-status-update"
.row-fluid
.time-ago-in-words
="#{time_ago_in_words(status_update.created_at)} ago"
Problem is status partial is not getting replaced once I click on the delete link, after refreshing the page ajax delete works for the first time then it again stops to work.
You are needlessly complicating everything. Your destroy controller could be simple as follows:
def destroy
#status_update = StatusUpdate.find(params[:id])
if #user == current_user
unless #status_update.destroy
redirect_to #user
end
end
end
In your views, instead of calling through $.ajax, try a rails link_to "action", action_path, remote: true
Name your html view file as destroy.js.erb.
These can be its contents:
$('.status-partial').html(<%= #user.generate_new_content.to_s %>);
Rails will take care of the rest. When you click on the link, the element with class 'status-partial' will have its html content replaced with "some new content".
It's a good practice to separate your HTML and AJAX responses, and Rails provides a convenient way to do it. Use it.
I think it would be better if you would do
respond_with do |format|
format.js
and then have a file called destroy.js.erb to place the javascript that should fire after a deletion
The issue was with syntax of .on used in javascript file, it does not work like .live. Changing the syntax to
$(document).ready(function(){
$(document).on("ajax:success", "#create_status_update_form", function(evt, data, status, xhr){
$('.status-partial').replaceWith(xhr.responseText);
$('.comment-text').val('');
})
});
solved the problem.
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..
I have a an application layout template with:
<%= yield(:railed) %>
To handle contact in the right rail (right column 300px). In the actualy DEF SHOW page I use:
<%- content_for(:railed) do -%>
HTML STUFF goes here
<%- end -%>
The issue I'm having now is that for one of my controllers, Im using AJAX to hit DEF Show, and inject the content into the page. This work fine expect for it doesn't get the railed content as the layout template isn't being used in the format.js response.
So what's a smart way I can get the railed contet display with the AJAX request, and not have to write to separate pages for AJAX & Non-AJAX (direct URL).
Thoughts? I know some Rails genius has figured this out already :)
My controller:
def show
#thing = Thing.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.js
end
end
show.js.erb:
<%= render :partial =>"thing/show" %>
It's a bit of a hack but this is how I did it in rails 3.1 with haml and jquery:
show.html.haml:
- if request.xhr?
#content_for_sidebar.hidden
=#view_flow.get(:sidebar)
ajax_callbacks.js:
$("#nav a[data-remote]").live('ajax:complete', function(status, xhr) {
$($(this).attr('rel')).html(xhr.responseText);
$('#sidebar').html($("#content_for_sidebar").html());
$("#content_for_sidebar").remove();
});