This is probably a really dumb question with a simple answer but...
I am working on a project where I need to use AJAX to send/receive information. I am using Ruby on Rails (which I am pretty new to), but rather than using the helper methods or the built in functionality in the 'defaults' Javascript file, I need to do this manually in my own Javascript.
I believe that ultimately I will want to send a request with a JSON object, then have the controller return another JSON object containing the requested information.
The problem is that I cannot seem to find the syntax for sending something to a specific controller method, with parameters, directly from Javascript - everything I Google ends up being tutorials for using the Rails AJAX helper methods.
Depending on the version of Rails you're using, you can do the following:
Install the jrails plugin. This will replace all the Prototype javascript in your application with jQuery. This means you now have access to all the jQuery libraries, but it won't break your existing rails helper stuff (remote_form_for, etc).
Now you can use the jQuery AJAX to make any AJAX requests you want to make. A simple example would be something like below:
//Let's get a specific post by id
$.ajax({
type: "GET",
url: "/posts/123",
success: function(data){
//put the data in the DOM
}
});
Then just add the appropriate respond_to in your controller:
PostsController < ApplicationController
def show
#post = Post.find(params[:id)
respond_to do |w|
w.json { render :json => #post.to_json }
end
end
end
if using jQuery is an option for you, jQuery.ajax will solve your problem.
[and for those who likes to say jQuery is not solution for everything, i know that. i'm just giving him an option].
Related
The last few days I've spent trying to understand different facets of AJAX on rails. After reading some introductions I've managed to get an idea of the rails built-in UJS feature. An example from a small toy app I wrote and where I want to introduce some AJAX-capability...
The controller action looks as follows
class ExpenseListsController < ApplicationController
before_action :require_authentication
...
def create
#expense_list = ExpenseList.new(expense_list_params)
if #expense_list.save
respond_to do |format|
format.html do
flash[:success] = 'Created!'
render :show
end
format.js
end
else
respond_to do |format|
format.html do
#errors = #expense_list.errors
flash[:danger] = 'Something went wrong!'
render :new
end
format.js
end
end
end
...
end
In my view I call the action via the remote: true option
The respective create.js.erb looks like this
var form_field = $('.expense_lists_form');
var expenseLists = $('#expense-lists');
expenseLists.append("<%= j render #expense_list %>");
form_field.slideUp(200);
The template for #expense_list looks like this
.col-xs-12.col-lg-3.col-md-4{id: "expense_list_#{expense_list.id}"}
.panel.panel-default
.panel-heading
= link_to expense_list.name, expense_list_path(expense_list)
.panel-body
.links
= link_to 'Modify list', edit_expense_list_path(expense_list), remote: true
= link_to 'Delete list', expense_list_path(expense_list), method: 'delete', remote: true
.description
%p
- if expense_list.description.present?
= expense_list.description
- else
%i
No description supplied, add one
=link_to 'here', edit_expense_list_path(expense_list)
.email-notification.text-muted
(Email notifications enabled)
.panel-footer
= "Expenses in #{current_month_name}:"
%b
= "#{expense_list.sum_of_exp_in_month(current_month, current_year)}€"
= "(#{expense_list.euros_left_in_month(current_month, current_year)}€ left)" if expense_list.budget_in_euro
It works but to me this idea seems to have some down-sides:
Bloats my file structure by having extra *.js.erb files
Distorts the physical separation of JS and the rest of the codebase
As I use HAML it introduces a new style of coding (ERB)
Now I have two questions:
Every tutorial (I've seen so far) seems to promote this kind of solution for handling AJAX responses in Rails: Why? When I check the code of other, larger rails projects (e.g. Diaspora) I do not seem to find them do it this way - most of them seem to handle it inside plain JS/jQuery via $.ajax({ ... }). So what would be the major advantages of the rails-internal UJS approach?
If the rails UJS-way is preferrable for some reason: How do you organise your code? Create extra directories for the *.js.erb-files?
What would be a good practice to transfer all this stuff to plain javascript files located in my /app/assets/javascript directory and handle the AJAX requests within jQuery there? How would my controller response have to look like in order to respond with the proper portion of HTML to update the DOM with via JS? In other words: How can I respond with a partial that I can handle in Plain Javascript/jQuery?
Thanks in advance!
Andi
So what would be the major advantages of the rails-internal UJS approach?
js.erb is a form of a poor mans Single Page Architecture (SPA).
It makes it just easy enough to return .js responses from your controller which modify the current page and lets you use the rails helpers for templating so that you don't have to use a client side templating system such as handlebars.
Note that this is not really internal to rails. jQuery UJS simple uses the fact that rails can return multiple formats of a resource. You could potentially use this with any MVC frameworks that can serve javascript.
The main advantage is that it is very approachable.
Its just enough for classic syncronous apps that want a few sprinkles of ajax here and there.
It gives developers who think jQuery.load and script tags everywhere is the best thing since sliced bread just enough rope to hang themselves with.
The cons
Violates REST as js.erb views are usually used as procedures to manipulate the current page.
the javascript in a js.erb view is not minified by the assets pipeline as it served per request.
It leads to horrible architecture decisions.
Whats the alternative?
Long before jquery-ujs entered the scene we had already figured out that the best way to do ajax requests is JSON.
So if you want to send a form asynchronously you would do:
$(document).on('submit', '.ajax-form', function(e){
e.preventDefault();
var $form = $(this);
var promise = $.ajax($form.attr('action'), {
accepts: { json: 'application/json' },
data: $form.serialize(),
context: $form,
method: $form.attr('method')
});
promise.done(function(response){
// handle the response
});
});
This way the javascript logic can be concatenated into a single file, and tested separately in javascript testing tools.
Your backend server just responds with simple data and does not concern itself with what the client does with it.
However this does require you to setup some sort of templating on the client side to handle converting JSON to HTML and you need to setup stuff like data bindings to have your form display errors. This leads to code duplication.
This is where SPA frameworks like Ember and Angular come into the picture which do all the templating and rendering in the client.
How can I respond with a partial that I can handle in Plain Javascript/jQuery?
You could create add additional formats which your controller responds to. You could for example register a "text/html-partial" mime type.
Or create additional routes or even use a query param (shudder).
However this is less than ideal for the exact same reason as js.erb - it leads to a crappy API as your controllers will become process instead of resource oriented. You will end up creating ridiculous controller actions just to pass html fragments back to the client.
On most tutorials out there, people tell you to create a js.erb template for every action you want to respond with javascript, which leads to having both an html.erb and a js.erb, for every action I want to work with AJAX, like this:
Is this right? Am I doing something wrong? Because it looks awful to me, there will be at least 20 files in each view folder, by default.
I think you are doing right. You are using Rails’ AJAX helper and it is a good practice. Some advantages of this comparing to the normal way of using AJAX:
JS code is shorter and cleaner, we do not need to write some repeated boring code such as $("form#id").on("submit", function(){}). We just need to write the main code to handle the response data.
Unobtrusive JavaScript: JS code is rendered from server side. It is not shown along with the html.
I think splitting to js.erb files actually makes the code more manageable. It is personal thought though.
I don't know how complex your project is so I am not sure but maybe you can refine to have less partial files. For example, I noticed that you have both delete and destroy actions. Index, new and edit views may not need the partial files. It seems that you also handle Json requests. It also makes the view folder bigger.
Yeah, it looks really awful to me too. But if you're responding to every method with javascript you'll have to create js.erb templates for each them.
Another approach would be, you'd want to respond with json instead of script. Where all your ajax code will remain in the client side javascript, and you'll be responded back with json data.
For eg. lets get data for an particular area
$.ajax({
url: "/areas/23",
dataType: 'json',
method: 'get',
success: function(response){
//OPTION 1
//response will have all the data
//handle the value from the response object in displaying
//OPTION 2
//If you set dataType: 'html' you can receive html partial from server and make use of it directly
$("#show-area").html(response); //response will have content of _show.html.erb
},
error: function(error){
console.log(error); //Print errors if it breaks
}
});
#Controller
def show
respond_to do |format|
#OPTIONS 1
format.json { render json: #area.as_json }
#Or have a json.jbuilder partial if you want to send data selectively, ;) There is no escape from partials/templates
#OPTION 2
format.html { render partial: "areas/show" } # will render _show.html.erb
end
end
That being said, I think it finally comes to personal preference. And your preferences will vary upon different scenarios. You can pick any one of those based on the case. Let me know if it helped.
I have a normal rails app website, but on some pages I need to use AJAX.
I already have some working AJAX Javascript code using jQuery, but so far I haven't used any rails helper to do that, writing strings corresponding to paths manually.
But is there a more convenient way to do it in javascript ? Suppose I have a javascript function which takes an ID as argument, and must call an AJAX action. So far I've been doing it this way
var url = "/tags/tagID"
function getTag(tag_id){
$.get(url.replace("tagID", tag_id) +'.json')
.fail(function(data){
alert('Oops error !');
})
.success(function( data ) {blabla ] )
}
Is it possible to rename the .js to .js.erb and use path helpers ? So I could get rid of this url variable and write
routes.rb
resources :tags
tags.js.erb
$.get(tag_path("tagID").replace("tagID", tag_id)....
Or is there a more convenient way to do this ? I only need very little AJAX, so I don't want to use a frontend framework (Angular, etc.), just jQuery
EDIT My scenario
A user searches for a given tag thanks to an autocomplete searchbar. This searchbar will return the ID somehow.
The user can select several tags this way, and their IDs will be stored in an array. Now, upon clicking a button, I want to send a query to a non-RESTful (with the ID array as parameter) controller action via AJAX. For now I will focus on sending one item at a time (so just one ID string), for it is easier/more reactive.
This action is actually going to look in my models for projects and ingeneers that possess this tag, and return a JSON with formatted results.
Yes, you can use *.js.erb to use Rails helpers. Rails provides some handy helpers to work with Ajax. Normally with rails you can use them by using the the tag remote: true.
In your case something like
<%= link_to 'Tags', tags_path(<tag.id>), remote: true %> (roughly),
Read more about using Rails helpers with Ajax here, and this explains it nicely.
Update
Rails is using CSRF token to validate requests (except GET), so if you are going to use pure HTML/JavaScript, you want to add the token to your request. Have a look at this post on the same.
I agree there is no out-of-the-box way of doing that, but there are few workarounds.
EDITED Question
I understand that I can't just throw Rails code into Phonegap and I have to write create some static pages and use JS to do the server communication for data. My question is how to write Rails code to handle http request to return a JSON with information?
Thanks
OLD Question
I built a Rails mobile app and I am interested in using Phonegap to turn it into a native iPhone and Android app.
I searched around and wasn't really able to find a good tutorial on how to do this. I watched the Quick Left video and I am confused why the extra middle wear code was needed. From my understanding, to use Phonegap I need to rewrite all the front end and data fetching in javascript.
If anyone could offer some insight or point me to some tutorial to how I could integrate my Rails app with Phonegap please let me know.
Thanks
I do not think this is possible. Ruby would need an interpreter and unless it is in the browser, it cant run. I think the only way is to use javascript
You can´t, but maybe you want to try Rhodes: http://rhomobile.com/products/rhodes/
Rails 3.1.x already returns JSON requests if you have a controller like this:
class EaracheMyEyeController < ApplicationController
def show
#earache_my_eye = EaracheMyEye.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #earache_my_eye }
end
end
end
The format.json { render json: #earache_my_eye } part will render the data as JSON if you visit the URL of the object, for example earachemyeyes/1.json
So, in your phone gap app you would call URLS GET method to receive data and add on .json on the end of the url to receive JSON formatted data.
Update:
A few things I learned recently about JSONP requests in phonegap. They are only GET requests. No posts.
However, you can append a _method=POST, and add configure.middleware.swap(Rack::MethodOverride,Rack::RestfulJsonpMiddleware to your conf/environments/(production/development/test).rb file(s)
Add something like this to your library:
https://github.com/quickleft/kbomb_phonegap_server/blob/master/lib/rack/restful_jsonp_middleware.rb
This allows you to send an actual GET request, yet it is read and processed as your _method=POST or whatever method you really need. You can't use the built in Rack::MethodOverride because it only implements POST, and nothing else (was meant to facilitate PUT and GET). So the file at https://github.com/quickleft/kbomb_phonegap_server/blob/master/lib/rack/restful_jsonp_middleware.rb builds a new Rack middleware that lets you use all HTTP methods.
I have a javascript bookmarklet which used to work as a single user mode. So how it used to work is I would click on my bookmarklet on the browser, and it would inject a remote javascript on my server. This second javascript in turn makes an ajax call to my rails server with a code that looks something like this:
$.post(
"http://192.168.1.2:3000/stuffs",
{stuff: JSON.stringify({"link":address})},
"json"
);
This code used to work when I was working on my project without an account. But today I added a devise authentication system. So now the system has users. In my controller code I have
before_filter :authenticate_user!
def create
puts current_user
#stuff.user = current_user
...
end
This doesn't work. current_user returns nothing. I am just trying to figure out whether there's an existing session (whether someone is signed in), and then want to create an entry under that user. In this case, a user has_many stuffs and stuff belongs to a user. The rest of the site works fine. It just seems trying to access using bookmarklet doesn't preserve a session.
Anyone know what's going on?
I believe you need to pass authenticity token parameter to params sent by jQuery, like this:
$.post(
"http://192.168.1.2:3000/stuffs",
{
stuff: JSON.stringify({"link":address}),
authenticity_token: <%= "#{form_authenticity_token.inspect}" if protect_against_forgery? %>
},
"json"
);
More docs on form_authenticity_token can be found here: http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html#method-i-form_authenticity_token