I have this code in my create.js.erb file:
pollingAJAX();
function pollingAJAX() {
$.ajax({
method: "POST",
url: "/status",
data: {uuids: '<%= #uuid_hash.to_json %>'},
},
success: function(data){
var obj = $.parseJSON(data);
if(obj.isDone == "yes"){
}else{
obj.each(function(result) {
if(result.status == "completed"){
$('a[href="#{result.url}"]').html('');
}
});
pollingAJAX();
}
}
});
}
This AJAX request is not being triggered because my create.js.erb file is not being rendered. I have this code in my create action:
result['items'].each do |r|
# escaped_url = URI.escape(r['link'], Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")).gsub("%","-")
#site_array << r['link']
if Result.where(:link => r['link']).present?
else
job_id = ImageGenerator.create(:url => r['link'])
#uuid_hash[r['link']] = job_id
end
end
respond_to do |format|
format.html
format.js
end
I think it may have to do with the fact that I am processing a bunch of background jobs with Resque before calling respond_to?
How can I get my create.js.erb file to be triggered?
UPDATE: Make sure you're actually including your JavaScript with a <script> tag, otherwise it will never be loaded.
Move the invocation of pollingAJAX() to after that function has been defined. Provided it (lexically) parses ok, JavaScript executes linearly, as it's interpreted, rather than compiled and then executed, so the pollingAJAX() function doesn't yet exist where you've put the invocation.
Also, always view your source code in the browser, to make sure the .erb has processed the way you intended. And make use of your JavaScript console in your browser to detect any errors.
Since it appears that you are using jQuery from your AJAX request syntax, you can bind your method to the DOM load event:
$(function() {
pollingAJAX();
});
This will prevent pollingAJAX() from executing until after the entire Javascript file has been parsed, allowing this function to find its definition.
See more here: http://www.learningjquery.com/2006/09/introducing-document-ready
Related
I am struggling w/ JS AJAX requests in Rails. There is an official guide here, but I am having slight difficulties matching it with ES6 JS. I am having troubles passing things back to my frontend after making my requests.
I have a JS window.onload call made, because I am trying to find the user’s screen size (among other things) and pass it back to Rails:
let xhttp = new XMLHttpRequest();
const url = "/users";
xhttp.open("POST", url);
// Some other things added to it...
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 201) {
console.log(this.responseText);
}
};
xhttp.send(JSON.stringify({user_info: userInfo}));
It is posting to /users some information about the session. This is going through fine. Note the console.log that keeps track of the response, we will get to this later.
In my Rails controller:
def create
user_info = params[:user_info].permit!
user_info = user_info.to_s
#fingerprint_user = User.find_or_create_by(fingerprint: user_info)
respond_to do |format|
# NOTE: I have tried a few things here
# format.html { redirect_to #fingerprint_user, notice: "Successfully identified user by fingerprint." }
# format.js
format.json { render json: #fingerprint_user, status: :created, head: :ok }
end
end
The JSON sender is working correctly. The console.log in the JS above correctly console.logs the received JSON. The request responds with 201, and the #fingerprint_user instance variable in JSON form.
My problem is with returning ERB JS with the instance variable. As shown in the guide, I have tried adding format.js. Then, the request returns a 200, and the contents of my views/users/create.js.erb file:
console.log("hello");
However, it is not actually logging to console.
Lastly, I tried with all format fields (js, html, and json). Here is my show.html.erb:
<p>Got user: <%= #fingerprint_user.to_s %> </p>
Here is a better views/users/create.js.erb file, where fingerprint is a div in my index.html.erb:
console.log("hello");
$("<%= escape_javascript(render #fingerprint_user) %>").appendTo("#fingerprint");
Once again, the response is 200, and the appropriate html, but this is not rendered on the page.
Doing requests for AJAX requests for JavaScript is different then requesting JSON. Instead of requesting some data and parsing it you actually load the data and then eval it into the current page context through various tricks like appending script tags into the document. This is the actual Rails UJS implementation:
processResponse = (response, type) ->
if typeof response is 'string' and typeof type is 'string'
if type.match(/\bjson\b/)
try response = JSON.parse(response)
else if type.match(/\b(?:java|ecma)script\b/)
script = document.createElement('script')
script.setAttribute('nonce', cspNonce())
script.text = response
document.head.appendChild(script).parentNode.removeChild(script)
else if type.match(/\b(xml|html|svg)\b/)
parser = new DOMParser()
type = type.replace(/;.+/, '') # remove something like ';charset=utf-8'
try response = parser.parseFromString(response, type)
response
This is basically how we used to do AJAX calls cross domain ten years ago with JSONP to get around the limitations of the browsers of the day.
You can emulate the same thing in a "raw ajax request" with:
let xhttp = new XMLHttpRequest();
const url = "/users";
xhttp.open("POST", url);
// Some other things added to it...
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 201) {
let script = document.createElement('script');
script.innerHTML = data;
document.querySelector('head').appendChild(script);
}
};
But quite frankly js.erb is a horrible idea. It makes an absolute mess out of the server and client side responibilities and makes your code very difficult to follow and reason about and it moves JS out of the assets/webpack pipeline and into a smattering of proceedural junk script views. The only possible reason to use it is how lazy you can be with Rails UJS and still add some ajax to your application.
If you're writing an ajax handler anyways just return a chunk of html (in a json object or as html) and append it to the DOM instead.
In my controller:
respond_to do |format|
format.html { redirect_to handlers_url }
format.js { render #handler, layout: false }
end
In my handlers.coffee file:
$ ->
$('body').on 'ajax:error', '#new_handler', (event, data, status, error) ->
console.log error
$('body').on 'ajax:complete', '#new_handler', (event, data, status) ->
console.log status
$('#new_handler').remove()
$(data.responseText).prependTo('tbody')
On the surface, this works in that the data (rendered using the _handler.html.erb file) is appended to the tbody element. But it won't work if I change ajax:complete to ajax:success, and the ajax:error fires every time with:
parsererror
SyntaxError: Unexpected token <(…)
It looks like the error is because I'm rendering HTML from the _handler.html.erb template and jQuery is expecting pure JS. But I'm not sure how to send pure JS without using a separate create.js.coffee file, which I'm trying to avoid.
Any ideas on how I can accomplish that in the controller without creating the extra javascript view?
You could try creating a .js.erb partial or file to handle the format.js response. You could copy the code from your current html partial and then use a jquery/javascript append/prepend/before method to place the generated HTML wherever you need to.
I have an action that calls a javascript file which contains an ajax method like this one:
$.ajax({
type: 'POST',
url: "<%= some_action(model) %>",
dataType: 'json',
data: { 'something': true },
success: function(received_data) {
// Do something with received_data
$('#notice').html("<%= escape_javascript(render 'layouts/flash_messages', flash: flash).html_safe %>");
}
});
The "some_action" tries to put some info into flash[:success], and I want to get it in the success function so that I can pass it to the render.
I have already tried the flash.now[:sucess], but nothing. It seems that it is only possible to do this if I write in the flash hash from the action that calls this javascript file - but I don't want this since "some_action" will generate dynamic content.
Is that something possible to to?
Thanks for the help!
you can send js request instead of json request .
and then in your "some_action.js.haml" file you can write
$('#notice').html("<%= escape_javascript(render 'layouts/flash_messages', flash: flash).html_safe %>");
what's happening here is that your javascript file is not getting refreshed hence content of html is not changing .
Note: I am presenting a logic here what I am doing.
What I am doing:
Think about the basic index action where we are listing products and with pagination. Now using remote-true option I have enabled ajax based pagination. So far things works perfectly fine. Take a look on sample code.
Products Controller:
def index
#products = Product.paginate(:order =>"name ASC" ,:page => params[:page], :per_page => 14)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #products }
format.js
end
end
Index.html.erb
<h1>Products</h1>
<div id="products">
<%= render "products/products" %> // products partial is just basic html rendering
</div>
<script>
$(function(){
$('.pagination a').attr('data-remote', 'true')
});
</script>
Index.js.erb
jQuery('#products').html("<%= escape_javascript (render :partial => 'products/products' ) %>");
$('.pagination a').attr('data-remote', 'true');
So whats the problem:
Now I want to enable action caching on this. But index.js.erb file is not manipulating DOM. If I remove the remote-true functionality then things works fine with caching.
For action caching I have added this line on the top of the controller:
caches_action :index, :cache_path => Proc.new { |c| c.params }
Any suggestions?
Update:
Problem is jquery code is not executing. From this question
I found out what's wrong. jQuery actually surround the incoming script with a so that the browser evaluates the incoming code. But the caching mechansim merely saves the code as text and when one re-request, it returns the code as text but not evaluate it. Therefore, one needs to eval the code explicitly
But how to solve this problem??
After some trial and error, I think I have a work around.
Page links
Instead of
$(function() { $('.pagination a').attr('data-remote', 'true') });
use
$(function() {
$('.pagination a').click(function() {
$.ajax({
url: this.href,
dataType: 'script'
});
return false;
});
});
so response created by the app server will be run as javascript
Controller
next, change your caches_action line to
caches_action :index, cache_path: proc { |c| c.params.except(:_).merge(format: request.format) }
since ajax appends a _ params for some sort of timestamp
Hopefully this works :)
I don't see what the issue should be with using remote: true. Someone else suggested to use .ajax instead of remote: true, but that's exactly what the remote functionality does, so there shouldn't be any difference.
The other answer has code that explicitly uses jQuery.ajax, but the only difference in their code compared to what the remote functionality does is that they're specifying an explicit dataType. You can actually do that with remote: true though.
In your HTML link, you just need to specify data-type="script". Or, based on your posted JS, you'd do this:
$(function(){
$('.pagination a').attr('data-remote', 'true').attr('data-type', 'script');
});
EDIT: Also, I wrote more in-depth about the data-type attribute and how it works with Rails here: http://www.alfajango.com/blog/rails-3-remote-links-and-forms-data-type-with-jquery/
I think I found a solution to this problem. I have a controller that has caches_action for an action that uses format.js to fetch some data via ajax, and it was not working out of the box.
I found that, despite the request being transmitted to the server, and the server correctly parsing the request and "rendering" the index.js.erb template, nothing was updating in the DOM. Your solution with $.ajax and dataType:'script' fixed the problem for me, however, I didn't like having to do jquery to bind to a click on a link, which should happen by default... I was able to make it work correctly by changing my link_to to this:
= link_to "click me", user_action_path(params), remote: true, data:{type: 'script'}
Hope this helps!
I've been having the same problem but on my 3.0.3 application with :remote => true I have added :'data-type' =>: script and have been worked fine.
However, in my case, I don't see improvement when loaded by ajax the list.
I have a problem when sending the Ajax request. When you click on the link request is sent to the server 3 times and the answer did not come.
Why the request is sent three times to undermine?
Where did I go wrong in the formation of a query?
code:
run.html.erb
...
<%= link_to "Next", "#", :id => 'next', :class =>
...
run.js.erb
(function(){
$("#next").click(function(){
$.ajax({
type: 'POST',
url: '/engine/tff',
success: function(data){
alert("ok");
$("#question").html(data);
}
});
return false;
});
});
controller
def tff
respond_to do |format|
format.js render :text => "hello"
end
end
I am guessing the click event is being bound multiple times probably cos the script is being called multiple times. Not sure what is the reason for the multiple calls as I am not familiar with rails.
what you could do to avoid it is unbind the click event before binding it or use the on api.
function ajaxRequest(){
$.ajax({
type: 'POST',
url: '/engine/tff',
success: function(data){
alert("ok");
$("#question").html(data);
}
});
return false;
}
$("#next").unbind("click").bind("click",ajaxRequest);
or
$(document).on("click","#next",ajaxRequest);
also not sure if you were trying to bind on document ready and there is a typo in your code. but it should be wrapped like this:
$(function(){
$("#next").click(ajaxRequest);
});
One thing I ran into recently was that jquery tries to run the result, as the default dataType interprets the .js as a script, not text. You might need to add a dataType: "text" to the ajax call. I don't see how this translates into three calls though.
I thought returning false was supposed to prevent the click from moving on, but perhaps it is better form to use preventDefault() as apneadiving suggests.