I'm confused about ajax request in rails.
I'm submitting a form using ajax and everything was fine. Now I must handle callback, but...
Following some tutorials the hint is to add a
respond_to do |format|
format.js
end
to controler#create and then place in the views folder a "create.js.erb" with the response.
Other tutorials suggest to create a js file in the assets folder and handle the callback from the JS file.
What's the difference? And what's the right way?
Both are different ways for handling callback of Ajax request. I suggest you to use js.erb file to handle ajax callback because this file can recognize the both JS as well as Ruby code and execute.
For example,
in your controller,
def create
...
if something
#foo = true
else
#foo = false
end
in your create.js.erb, it can be done like you want to show/hide some div based on the #foo value, you can write like,
<% if #foo %>
// do something with JS code
<% else %>
// do something else
<% end %>
While if you create a JS file in assets folder then you will not be able to write condition like above because it is a pure JS file and creating js.erb is an easy way to handle ajax response.
They are two very different approaches. For example, assume that all you want to do is display a message confirming the user's submission. If you go down the create.js.erb route then that file might look something like
$('#message').text('<%= j #message %>')
The response to your ajax request is a bit of javascript that does the required changes to the ui. jQuery executes that javascript when it receives it and your javascript doesn't have any visibility into what the response might do.
The other approach would be for your controller just to render some data. This could either be some JSON describing the results of the submission, or a block of html. For example your controller might do
render json: {status: 'success', message: 'Thank you for your submission'}
The javascript making the xhr might look like
$.ajax('/some_url', {dataType: 'json'}).done(function(data){
//data is the json your controller sent
$('#message').text data.message
});
or if you were rendering html
$.ajax('/some_url', {dataType: 'html'}).done(function(data){
//data is the html your controller sent
$('#message').html data
});
So in this case it's your javascript that decides what to do with the returned data.
Personally I dislike the create.js.erb route - I find it makes my javascript harder to read: I can't tell from looking at my form submission code what can/may happen in this callback.
It's also much harder to test if controllers are producing a blob of something that needs to be executed in a different language and its much harder to test your javascript if there are places where it executes arbitrary code sent to it.
I much prefer it behaving like an api, returning structured data, that the client can act appropriately on (the client might not even be a browser all the time, it could be a mobile app).
Related
I'm curious how Rails 5 renders partials and if there's a gotcha that I'm not aware of. I have two partials on my page, one that is rendered as part of the html and the other that renders inside the success callback of an ajax request. However, when I refresh the page, the partial inside the ajax request is actually being rendered even when the ajax request is not fired, and EVEN when the call to render the partial is commented out.
I had always assumed that partials render at the time the line of code is run.
Here is the relevant code:
$('body').on("click", ".notif-popup-container", function() {
$.post(
"/notifications/read",
{ notif_id: notifId },
).done(function(data) {
$('#notifUnreadCount').html(data.unread_notifs);
var popover = $('#notificationsBell').data('bs.popover');
popover.config.content = $('.notifications-list').html();
})
$('.notifications-list').html("<%= j (render partial: 'notifications/partials/test_partial') %>"); // this never fails to render, even when commented out
})
<div style="display:none" class="notifications-list">
<%= render 'notifications/partials/popover_notifications' %>
</div>
Here is the console output image. As you can see _popover_notifications.html.erb renders as it should, but then my _test_partial.html.erb also renders, which it shouldn't, since the click handler it's in was not fired.
Any insight appreciated.
I had always assumed that partials render at the time the line of code is run.
The key here is understanding when the code is run. When you dynamically create javascript with js.erb the file is first run through ERB on the server before it is sent to the client which runs the resulting javascript. Your server does not actually parse the javascript so it does not know or care that there is a event handler involved. To Rails its just a string buffer.
This is actually a major conceptual problem with js.erb templates as the context switching between server and client side makes the flow hard to follow.
There are also numerous other problems with this code like the use of the === identity operator and the fact that your performing DB queries in the view which is a huge anti-pattern.
I am doing an HTTP request against a remote API in my controller such as this:
#results= JSON.load(open("http://someendpoint/whatever.json"))
I am then rendering the contents of the response in my view by iterating over the structure provided in the response:
<% #results['result'].each do |result| %>$
This works 100% fine from a technical standpoint. The problem is that the initial JSON load in the controller appears to block the controller's thread (prior to rendering the page). There is additional information on the page that I would like to load quickly (i.e. without the controller thread blocking). Basically what I'd like to do is make the HTTP request asynchronously, and then render the JSON structure via callback. I can put the information in a partial if that would make things easier.
I can definitely use something like an XMLHttpRequest to concatenating the results of this (with interpolated HTML elements) into a string, and then just using DOM insertion methods to place the data into the view. So, I suppose my questions are this:
Is there a good way to do this asynchronously, for example by using a Javascript a callback to render a partial from a variable declared in an XMLHttpRequest? Is it bad to declare variables in the view and then render them in a partial? This just seems like bad design to me. I basically don't want to generate a long HTML string after an XHR request and inject it into the DOM because it seems 'sloppy'.
I appreciate your help.
Would it not be simpler to put #results in it's own controller action with it's own endpoint and load it via AJAX?
My Problem
I am developing a Rails site and would like to use Javascript to render partials in some of my views depending on certain conditions. For instance-- I'd like to have a partial view of a Devise log-in/sign-in prompt come up in a modal box if the user is not signed in when accessing certain pages.
I've had a lot of trouble figuring this out-- the first issue was that I tried using render in the asset pipeline which after some research found doesn't work to begin with.
I then tried putting a js.erb file into my public/javascripts folder but javascript_include_tag force appends '.js' to the file name and using regular src=/javascripts/... didn't render the '.erb' stuff but rather it would append the text <%=j render :partial ... %>
My Solution
I have come up with this solution here and would like to know if there is a better solution to keep clean code. I am going to have a few other Javascripts that will render the same thing over different views.
I have created a app/views/shared/javascripts directory where I will put [filename].html.erb files.
To get the Javascript to run correctly I will <%= render :partial => 'shared/javascripts/...' %> wherever I want that script to be run.
Inside that script is something like:
<script>
$(document).ready(function() {
...
$(.class).append("<%=j render :partial => 'shared/modal' %>");
...
});
</script>
Is There a Better Way?
As far as I can tell-- this will do what I want it to do. I'm just afraid that I'm looking at this all wrong. I'll be working on another part of the app for a while and I really hope to either verify that this is acceptable and decent or find the proper way to ensure that I can use ERB in my JS files.
I know this answer is 3 years late for the OP, but I found this question googling something else and thought I'd answer it as it's something we do quite a lot in our legacy app.
Suppose you want to render the javascript alert("Hello, world!"). You can create the partial _greetings.js.erb in the folder app/views/my_resources:
alert('<%= #msg %>')
And call it from a controller action:
#msg = "Hello, world!"
render partial: 'my_resources/greetings', formats: :js
Hope that helps someone.
One way to do this is to put your partial in your javascript and render it using something like Mustache. The problem with this approach is that if you're rendering a partial you're also rendering traditionally elsewhere in the Rails app, the Mustache template could easily get out of sync with the Rails partial.
Another way to do this would be to have an action which returns the rendered partial as a string, either as XML or wrapped in JSON, and use AJAX to request the rendered partial from that action. Then your Rails controller will handle both rendering the template (using render_to_string) and wrapping it in JSON for your javascript to consume and redisplay.
I followed Hartl's Rails tutorial where he does Ajax using RJS and sending javascript in the response to be executed on the client side to edit the DOM.
But what do you do if you want just JSON sent in the response and not send javascript. This also means the javascript to manipulate the DOM should already be in the html file on the client. Is there a tutorial as good as Hartl's book on how to do this in Rails. Presumably it would use Jquery and some other stuff maybe that I've not heard of to make the code not be a million lines?
My best attempt at an answer is that it really depends on the scope and complexity of what you're trying to achieve. Generally, JSON shows up in views. If your application does not require you to dynamically retrieve JSON, that is, you can load it all when the view is initially rendered, then you can set an instance variable in your view's controller like so
#my_json = some_object.to_json()
Then, your instance variable is available in your view
<script type = 'text/javascript'>
var theJSON = <%= #my_json %>
</script>
Now, your data is available in the DOM, parsed nicely into JSON.
If your application requires you to dynamically retrieve JSON after the controller/view are loaded, then you should probably look into using AJAX to hit a particular controller's method that returns the JSON that you desire.
Here's a good RailsCast that can hopefully help you along your way Passing Data to Javascript
You should take a look at Ajax in Rails 3.1 - A Roadmap.
I recently started working on a project which involves ruby on rails/javascript/jquery. I am very new to all this. The part I am involved is like this. User clicks on a button. A dialog will open, where user will enter a term to search. With that term, I am will be constructing an URI by looking at an xml. Then I will do a REST call. The response will be a XML file. I will be parsing it and displaying data in the same dialog where user entered term to search.
I am using JQuery to create a dialog. And I am doing parsing and REST call part in ruby code (Helper class). I am not using models and controller for my part. Now I need to send data from helper method to erb file where I will be displaying data as a table. Output will be like this
string11 string12 string13
string21 string22 string23
string31 string32 string33
. . .
. . .
For now, in ruby code I have create 3 arrays for every column. I am not sure on how to send data from ruby to html/javascript. If there is better way to do this then let me know. For ex: instead of sending as 3 separate array, is it better to send as xml/json? Or any other suggestions will be really helpful. Mainly I am not understanding on how to send this data to the erb file. Thank you in advance.
Edit: Adding a sample code
Javascript/Jquery -
On button click, I want to call a ruby method in erb file
Ex: <%= getUserList%>
erb file is also having other javascript/html code to display other stuffs.
And in helper method I have getUserList method
def getUserList(search)
uri = create_url(search)
#doc = Nokogiri::HTML(open(uri))
//Doing parsing and other stuffs here
//creating 3 array as mentioned above which needs to be displayed
end
I want above arrays to be displayed. I am not sure on how to send these arrays to the erb file which invoked this method
In your controller:
def users
respond_to do |format|
format.js do
render(:js => "callback(#{#users.to_json});")
end
end
end
Use something like this in your HTML to call that:
<%= link_to_remote "get users", :url => { :action => "users" } %>
More information on rendering vanilla JavaScript from a Rails action: http://guides.rubyonrails.org/layouts_and_rendering.html#rendering-vanilla-javascript
This example constructs JavaScript that calls a "callback()" function in your JavaScript code, with JSON data. Your callback function will receive a JSON string that will be easy to parse. You could also use to_xml to send XML if you really want, but parsing JSON is much simpler and faster.
You don't need a helper in this example because the JavaScript call that includes the data will come directly from your controller. There is no view template, so you don't need a helper. The best place for the code that gets the #users would be in a User model class. Not an Active Record model, just a plain model. The code that fetches the users from the remote REST API could possibly be a class method. (If you're feeling lazy then you could set #users with the REST API response right there in the "users" controller action. But that's not ideal use of Rails because the whole point of Rails is MVC.)
If you're using jQuery, then you could also use jQuery's getJSON() function so that your controller would send only the JSON data without using the callback: http://api.jquery.com/jQuery.getJSON/
Note that this is the simple kind of answer that you seem to be looking for, but this isn't really the best way to do this kind of thing for more complex applications. The best way to do this kind of thing at the moment is with something like Backbone.js. http://documentcloud.github.com/backbone/ With Backbone.js, you can define a "users" collection in JavaScript, set the REST URL of that collection (it can be a URL in your Rails app, which relays data from some other API) and then you simply call users.fetch() in your JavaScript to load the data. It's very similar to Active Record, but for the client site. It's better because it abstracts the process and allows you to update and delete records as well as simply listing them.