I saw this post about this issue:
Auto Refresh a Html table every x seconds
Now, i am using rails, and i would like to do the same thing, but i want to tell rails that i want a remote page, i dont want it to load the full page.
All i want is pretty much to simulate :remote => true action with ajax.
I've tried it with regular ajax, and rails loads the whole page instead of just the relevant content that i want.
How can i do it ?
Your code should be something like this:
Your controller
def your_action
# your code goes here
end
(your_action.js.erb)
$("#div_id").html("<%= escape_javascript reder :partial => "your_partial_for_table" %>")
your_action.html.erb
<div id="div_id">
<%= render :partial => "your_partial_for_table" %>
</div>
_your_partial_for_table.html.erb
#your table goes here
<script>
$(function() {
$(document).ready(function() {
setInterval(function() {
jQuery.ajax({
url: "<path to your_action>",
type: "GET",
dataType: "script"
});
}, 30000); // In every 30 seconds
});
});
</script>
Server Sent Events
You may wish to use Server Sent Events (from HTML5):
Server-sent events (SSE) is a technology for where a browser gets
automatic updates from a server via HTTP connection. The Server-Sent
Events EventSource API is standardized as part of HTML51 by the W3C.
This basically initiates something called ajax long-polling, which is where your JS will "ping" a particular URL every second, or number of seconds.
The Ajax polling requests will then bring back particular response from the server, allowing you to use as you wish
--
Layout
I would do this:
#app/assets/javascripts/application.js
var source = new EventSource('path/to/your/endpoint');
source.addEventListener('message', function(e) {
//do something here
}, false);
#app/controllers/your_controller.rb
Class YourController < ApplicationController
include ActionController::Live
def your_action
response.headers['Content-Type'] = 'text/event-stream'
sse = Reloader::SSE.new(response.stream)
begin
sse.write(last_updated, event: 'results')
rescue IOError
# When the client disconnects, we'll get an IOError on write
ensure
sse.close
end
end
end
The trick here is that SSE's use their own content type - text/event-stream to receive & parse the required data. This is compared with the standard mime types which governs the typical HTTP protocol
I used this url for reference - you can then manipulate the text as it comes back from the server!
/* with jQuery */
jQuery(documenta).ready(function() {
setTimeout(function() {
jQuery('.table').load(...);
, 5000);
});
# with rails, determine if it is a ajax request
if request.xhr?
# respond to Ajax request
else
# respond to normal request
end
see also here Rails detect if request was AJAX
Related
Hi I want to create a page where it monitors(view) or render the value of the variable inside the controller dynamically as the iteration of variable goes by.
view.html.erb
<a id='get_value' class="btn">Run</a>
<ul id="value_variable_from_controller"><ul>
<script>
var getTheValue=function(){
$.ajax({
type:'GET',
url:'/run/results',
success:function(data,status){
<!--how to dynamically UPDATE the value_variable_from_controller?-->
alert("Successfully");
}
});
}
$(document).on("click","#get_value",getTheValue);
</script>
And here's my controller where i iterate the value of x,
results.rb
x=0
5.times do
x++
sleep(1)
#HOW TO RETURN THE VALUE OF VAR X EVERY UPDATE?
#render :json => x, :status => :ok #do i need to have a loop in js?
end
Many Thanks.
If I understand you correctly you want to update the value in the HTML document every time it is updated on the server, correct? There are ways to accomplish this, although the transitioning of state between client and server is usually initiated by the client. 1
To combat this, websockets are currently the most popular approach - basically opening up a two-way communication pipe between the client and the server. Of course, for your simple example this might be overkill (unless you require heavy real-time interactions between the client and the server - then it's certainly worth taking a gander at) - and something like polling might be more suitable (which is indeed initiated by the client), although it will generate a large number of requests.
You might also consider long polling which only opens one connection.
Polling is a technique in which the client calls the server requesting data at a certain interval, which would work for your situation. From the code you posted it also seems like you want it to be possible to fetch the value by clicking on the #get_value link, which can utilise the same method as the long polling service. Here's an example:
// in your view/javascript
$(document).ready(function() {
function getValue(trigger) {
$.ajax({
type: 'GET',
url: '/run/results/',
success: function(data, status) {
$('#value_variable_from_controller').text(data.x);
if(trigger) {
setTimeout(getValue(true), 1000); }
}
)} // end AJAX
} // end getValue
// binding the action to your link as well
$('#get_value').on('click', function(event) {
event.preventDefault();
getValue(false);
});
// start the polling
getValue(true);
});
# in your controller
#x = 0 # initial value
def results
#x++ # increment every time this action is called
render #x.to_json
end
This way the client initialises the data flow, and the server mutates state in response to a client request - which is the norm in a client/server architecture.
1 The client–server characteristic describes the relationship of cooperating programs in an application. The server component provides a function or service to one or many clients, which initiate requests for such services. (http://en.wikipedia.org/wiki/Client%E2%80%93server_model)
I am trying to notify the browser of the user of a change of the status of the model. I am trying to use the live-module of Rails for that. Here is what I have got so far:
require 'json'
class Admin::NotificationsController < ActionController::Base
include ActionController::Live
def index
puts "sending message"
videos = Video.all
response.headers['Content-Type'] = 'text/event-stream'
begin
if(params[:id].present?)
response.stream.write(sse({id: params[:id]},{event: "video_encoded"}))
end
rescue IOError
ensure
response.stream.close
end
end
private
def sse(object, options = {})
(options.map{|k,v| "#{k}: #{v}" } << "data: #{JSON.dump object}").join("\n") + "\n\n"
end
end
The idea behind the above controller is, that when its url gets called with a parameter, it would send this parameter (in this case the id) to the user. Here is how I am trying to call the controller:
notifications_path(id: video.id)
Unfortunately though, the following event-listener in the browser does not fire, even if I use curl to provoke an event:
var source = new EventSource('/notifications');
source.addEventListener("video_encoded", function(event) {
console.log("message")
console.log(event)
});
The goal of this is, that I want to add an dom-element to a certain page (later on) if there is a change. There may be a better way, but Ruby Live seemed like a suitable solution. Any tips or proposals of a different approach are appreciated.
Your use case does not seem like a valid use case for ActionController::Live. You are not sending a streaming output to the browser. You do a one time check on ID and send the JSON output.
Use a regular controller and get the request by AJAX instead of EventSource.
Check this out. I've got a fairly simple form that's created with the following syntax:
<%= form_for([#issue, #issue_order], :remote => true) do |f| %>
The form, due to logic on the page, is actually called via javascript, like this:
$('#new_issue_order')[0].submit()
The controller handles the ajax request by doing a bit of logic then throwing out a little something like this:
respond_to do |format|
format.js
end
The AJAX that handles this response is in the following javascript:
$('#new_issue_order').on('ajax:success', issueOrder.processOrder)
..........
processOrder: function(e, data, status, xhr) {
$('.sign-up-errors').empty();
errors = xhr.getResponseHeader('X-Flash-Error').split(',');
for (i=0; i < errors.length; i++) {
$('.errors').append($('<p>' + errors[i] + '</p>'));
}
setTimeout(function() {
$('.errors').empty();
}, 3500);·
}
I figured that would allow it to respond to the remote request, but what I get instead is the following error:
ActionController::UnknownFormat
I tried creating a new.js.erb in my views (to correspond with the new page that it was on), but I'm still getting the same error. I haven't tried migrating my success handler AJAX to the new.js.erb code, because I'd prefer to keep my javascript handling in the javascript file in my assets for business reasons.
How can I get a seamless AJAX response? I've done it before, but respond_to has always confused me.
Setting the js response template as new.js.erb is incorrect.
The form itself is within new.html.erb template, guessed by convention. So, the form's action is supposed to point to #create.
In order to response correctly to this form's submission, you need to create a js template as create.js.erb, and respond to js in #create action.
Besides, in most cases you don't need to manually set Ajax response in assets js like
$('#new_issue_order').on('ajax:success', issueOrder.processOrder)...
Instead, you can just do it within create.js.erb. For example
$('#new_issue_order').css('background', 'green')
This script will be run after ajax:success event.
I have an action triggered by an AJAX request generated by Ajax.InPlaceEditor or InPlaceCollectionEditor like this:
new Ajax.InPlaceCollectionEditor('agent_email', 'inspections/<%= #inspection.id %>/update_field',
{
collection: [<% #agents.each do |agent| %>
'<%= agent.email %>',
<% end %>],
okText: 'Update',
cancelText: 'Never mind',
savingText: 'Updating...'
});
At the other end, the action contains this:
def update_field
--some code here--
if success
puts "stored change"
render :text => result
else
puts "did note change store"
render :text => inspection.errors.to_json, :status => 500
end
end
Once any of the render methods are reached, the session expires, and next time the user send a request, Devise sends them to the logon on page.
Even though I am exempting update_field from authentication (before_filter :authenticate_user!, :except => :update_field), the session is still getting reset.
I have looked at the answer at a very similar question at Devise session immediately expiring on .js call [AJAX], but it is not solving my particular problem.
Any ideas?
I got this to work by getting the code from http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails (prototype-snippet.js):
/*
* Registers a callback which copies the csrf token into the
* X-CSRF-Token header with each ajax request. Necessary to
* work with rails applications which have fixed
* CVE-2011-0447
*/
Ajax.Responders.register({
onCreate: function(request) {
var csrf_meta_tag = $$('meta[name=csrf-token]')[0];
if (csrf_meta_tag) {
var header = 'X-CSRF-Token',
token = csrf_meta_tag.readAttribute('content');
if (!request.options.requestHeaders) {
request.options.requestHeaders = {};
}
request.options.requestHeaders[header] = token;
}
}
});
... within a Javascript block in my application.html.erb:
<script type="text/javascript">
(... the code from above)
</script>
Also don't forget to add:
<%= csrf_meta_tag %>
in the same file towards the top (if not already there).
The document "CSRF Protection Bypass in Ruby on Rails" explains why this works.
I'm having a little trouble using jQuery in Rails.
I'd like to call the destroy method for a specific list item and then remove it from the list via ajax. My code is pretty simple:
# _web_profile.html.erb - The partial containing the link to destroy:
<%= link_to 'Remove', web_profile, :method => :delete, :class => 'remove_button' %>
# The ajax hijacking
$('.remove_button').live('click', function() {
$.ajax({ type: 'delete', url: this.href });
return false;
});
# In my controller
format.js { render(:update) { |page| page.remove "web_profile_#{id}" } }
Ok, basicly that's it. When I press my button everthing works fine, but instead of executing the script I'm getting it a text output in the browser:
# Browser output
try {
jQuery("#web_profile_12").remove();
} catch (e) {
alert('RJS error:\n\n' + e.toString());
alert('jQuery(\"#web_profile_12\").remove();'); throw e
}
Any idea why this nice javascript code isn't executed? I tried to add dataType to the ajax request already.
Thanks and best regards,
Joe
Why "type" is set to "delete"? It should be set to "POST" and dataType to "script". Because of cross browser compatibility isssues, a workaround should be used to specify action as the RESTful delete. The data should have "_method" parameter set to "delete".
I suggest to install jrails and use link_to_remote and other Rails helpers. They generate correct JS code. So you can learn how to build correct Rails Ajax requests.
You have to either set the type to "script" in the $.ajax() call, or replace that call with $.getScript().