Returning HTML to AJAX Rails calls - javascript

After reading David Heinemeier Hansson's blog post about server-generated javascript I decided to review the way I approach making AJAX calls in my Rails applications. What David suggests is to create a .js.erb template, which is just javascript embedded with ruby code generated on the server, and not to do any DOM manipulation in the client-side javascript.
Another approach is of course to simply do everything on the client-side, and (for example) return a JSON object representing the updated object from the server and use javascript to do all DOM manipulation.
I dislike the first approach for two reasons:
1) I use HAML and Coffeescript in my application, and feel that by using vanilla javascript and ERB would uncecessarily bloat my code-base with code of a different language (maybe it's possible to create .coffee.haml templates rather than js.erb, I don't know)
2) I really don't like the idea of 'littering' my view folder with what is essentially javascript files, with a little bit of ruby embedded.
The second approach, as David talks about in his blog post, relies very heavily on client-side javascript, which could lead to bloated client-side javascript code, and possibly the need for client-side templates, which in worst case scenarios could mean almost doubling the number of templates.
The approach I decided to go for (and want to ask whether or not is completely stupid way to go about this) is the following:
1) Set the remote: true flag to make links and forms utilize AJAX to post to the server.
2) In my controller, handle everything as html, and simply render without layout should the request be an AJAX request: render partial: '<partial-name>', layout: false if request.xhr?. This simply returns the HTML of the partial, with the ruby code evaluated.
3) In an asset javascript file (for instance <partial-name>.js.coffee) listen to ajax:success and append the HTML from the response.
I like this approach because (in my rather simple application) this allows me to keep all my code in HAML/Coffeescript, and avoids any javascript templates.
I realize this problem might take on a different character should the complexity of the application grow, but I still feel that it's a valid question: is this a bad way to go about implementing an AJAX-based architecture for a Rails application (and if so, why? i.e. is there a reason why returning HTML instead of JSON from an AJAX call is a bad idea?) or is this something I should continue to utilize?
Thank you :-)

Your approach seems to me quite accurate. However I would change 1 pt. Instead of using remote: true, I would use jQuery ajax call directly.
$(function(){
$('#some_link').click(function() {
$.ajax({
// some parameters here
})
.done(function(data){
$('div').html(data);
});
});
});

if you use remote: true it sends a js request instead of html request.
then when the controller method is executed, it looks for the js.erb or js.haml file which has the same name as the controller action that just finished executing.
in this file you can write 'js' code to perform any action that needs to be done after the controller action has completed executing like changing a partial or updating view, etc.
if you have a function in javascript asset file, you can call that function as well.

Related

Call JavaScript function from Ruby on Rails model

I have built a Rails app that has an API that returns a JSON object. I want to allow users to write their own scripts and store them. These scripts will then be executed when the user makes an API call to the app and injected as part of the response.
An example would be when a user calls my API, their javascript would be called and whatever is returned from their script would be saved as a string and used by one of my models and used as part of the JSON object - not the full response.
I want to know how to call a javascript function from a rails model, save the output of the javascript as a string in Ruby, and then be able to process whatever is returned before making the API response.
The current answers seem to only be related to the controllers and rendering as JS, but I would like to execute the script from the model.
It should be something like:
string = execute_javascript("return 'hello world';")
This is more related to pure Ruby than Rails as I am just wanting to call it from a standard Ruby class and save the output as a string. I also need to consider the security implications of doing this, but first would like to know how to do it
The simplest solution to execute most common Javascript code is using ExecJS gem. If you are using the Rails standard gem bundle then ExecJS is already included with Rails Coffeescript. A short example:
require "execjs"
ExecJS.eval "'red yellow blue'.split(' ')"
# => ["red", "yellow", "blue"]
The above solution is only good if you are running a prototype. In production, you would want to somehow run the user code in sandbox and preferably on the whole different server. You would also need to ensure the code won't take 100% resource of your server while executing and screen the code to ensure it won't do anything malicious.
You can call javascript function in Helper class and then include that helper inside your model where you need it.

Is it correct to have files that mix Javascript and ruby? ie. (.js.erb)?

I've recently read that embedding ruby inside JavaScript is not a good idea.
However, in books such as David Heinemeier Hansson's Agile Web Development with Rails, that's exactly what it does.
If embedding ruby with JS is NOT a good a idea, then what's the best practice for such case?
Given something as simple as this: (jQuery + ruby)
posts_controller
def create
#post = Post.new(params[:post])
respond_to do |format|
if #post.save
format.html { redirect_to(#post, :notice => 'Post was successfully created.') }
format.js #will use this response to process ajax
else
format.html { render :action => "new" }
end
end
end
create.js.erb
$tr = $('<tr>');
$td1 = $('<td>').text('<%= #post.title %>');
$td2 = $('<td>').text('<%= #post.content %>');
$tr.append($td1, $td2);
$('table tbody').append($tr);
How should it be refactored to follow the "best practice" of not embedding ruby with JS?(if that's the case)
I really need to be enlightened on this, and maybe get the concepts right because I have read that rails 3.1 will separate JS from Ruby completely through assets ?(Is this correct?)
Thanks !
RJS templates were essentially the way things were done for a long time, which is why you still see such a prevalence of the technique in tutorials. That being said, as you've noticed, they're on the way out, and with good reason.
I'm sure there are many reasons why RJS templates are a very bad thing, but a big one is how tightly they couple your view JS to your view HTML. Many problems arise from this, a few being:
Lack of flexibility.
Using your bit of code as an example. What if you wanted to be able to create posts from a different view? And have a different effect? Maybe there's no <table> on the page at all and you just want to pop up a "success" message? Etc.
What if you just wanted a data representation of your posts, but your javascript templates were written to manipulate HTML?
How do you handle all this in one create.js.erb, which is tightly coupled to, most likely, the posts new.html.erb template?
Complexity.
RJS templates operate in somewhat of a vacuum. Generated on server side, their execution is not bound to DOM events. This makes it tricky to do things like, say, update a <form> on a page after an object is being created, as you have no frame of reference to select the appropriate <form> in the JS template (e.g. <form id="new_post_123">). There are workarounds for this, but they're more convoluted than they need to be.
By binding to a form client-side and dealing with the result, this problem is eliminated. You don't need to find the proper form to update after a response.
With RJS you have no such binding, and you're forced to use the known structure of the HTML to retrieve and manipulate the appropraite elements. One example of unnecessary complexity.
Regarding your question:
I really need to be enlightened on this, and maybe get the concepts right because I have read that rails 3.1 will separate JS from Ruby completely through assets ?(Is this correct?)
This is essentially true. The asset pipeline, while cool, doesn't offer anything brand new. Asset frameworks like Sprockets and Sass, and the workflows they enable, have been around for a while. The Rails 3.1 asset pipeline just brings them into standard practice. It's more a matter of perspective, as DHH talked about in his recent RailsConf keynote. By bringing greater organization to these assets, js files in particular, it makes them feel more like first-class citizens, so to speak, deserving of as much attention as your back-end code. As opposed to the "junk drawer" feel of public/javascripts.
As for the implementation:
You might do it something like this (although untested and a bit simplified, e.g. in Rails 3 you might use responders for this rather than a respond_to block:
# as you have, but returning the object as data which can be handled client-side,
# rather than RJS generated by the server
def create
#post = Post.new(params[:post])
respond_to do |format|
if #post.save
format.js { render :json => #post }
else
# ...
end
end
end
// Then in your client side JS, a handler for your remote form.
$(function(){
$('#your-create-form').bind('ajax:success', function(data, status, xhr) {
// simply repeating what you had in the template, replacing the erb with
// attributes from the returned json
$tr = $('<tr>');
$td1 = $('<td>').text(data.title);
$td2 = $('<td>').text(data.content);
$tr.append($td1, $td2);
$('table tbody').append($tr);
});
}
If you embed dynamic data in JavaScript files, then you often have to give it caching rules suitable for content that can change frequently.
If you need to process dynamic content with JS then it is usually better to have a static, cacheable, reusable script that gets data from elsewhere.
In this specific example, although I'm not sure since there isn't a great deal of context, it looks like you would be better off generating HTML directly instead of generating JS that generates HTML.
I'm sure you can find this question asked by beginning PHP devs, Perl, ASP and whatever other server-side languages there are out there. There don't seem to be too many cases where you'd want to have your javascript code inline.. the only one might be where you're customizing it to a particular situation, but off the top of my head I can't think of any situations where you'd need to customize it like that.
Peformance-wise, if you have a lot of javascript and you include it all in your output, you bloat your page. A 1k page will turn into a >100k page if you're adding a big application in. And, along with that, it will likely be more difficult to cache that information since you're probably customizing the output just a little bit if the user is logged in, or if there's a special ad out that day, etc. You're much better off splitting it off into its own file so that you can "minimize" it (http://en.wikipedia.org/wiki/Minification_%28programming%29) as well.
From a developer to a developer, I can give you horror stories of having javascript (and html) embedded in both PHP and Smarty templates. Be nice to whoever you're working with and split all the languages out into their own files. Let's say you're working with Bob and he's a great javascript guy.. but doesn't know a thing about the application and how it spits out its code. Having javascript smattered in the erb's is going to be frustrating due to the lack of a single point of truth for the application - if he wants to tweak something, he's going to be looking through all your erb's to figure out where exactly he has to put it or where was it originally. And do you really want someone who knows little about your backend's structure to be poking around through those files?
There's a couple of cases though where you'd want to put bootstrap data in.. if you've got a lot of data to inject to the page on startup, having that assigned to a few variables when the page starts is more performant than going out and making an ajax request on every page load.
Please be nice to us front-end guys and unless you've got a real good reason.. keep the JS in separate files :)
Edit: Here's an example of doing what you want (I think), without really using templated javascript:
app.js:
postUpdater = function (data) {
$tr = $('<tr>');
$td1 = $('<td>').text(data.title);
$td2 = $('<td>').text(data.content);
$tr.append($td1, $td2);
$('table tbody').append($tr);
};
$("#save_button").click(function () {
$.post('/save/ur/', {the:data}, postUpdater);
});
In ruby, you'll want to just render the #post to json.. I think it's literally #post.to_json, but you may have to require 'json' in order to get it to work. This might get you there faster though: output formated json with rails 3 (I'm not really a ruby guy.. we just use it at the company I work for so of course I've been picking it up)
Now, as for a theoretical on why not to just output a templated javascript file. Let's say you've got a whole bunch of stuff that needs to happen when you save that javascript file. That means you've got to output a whole lot of stuff on that ajax request and execute it. You'd be pushing a larger file over the internet (a tiny bit slower) and possibly having to 'eval' a response (eval is evil). So, that's one bad thing. Another possibly bad thing is that you lose access to where it was you called the save from. So, if I've got this view..
new PostViewer({
savePost: function () {
var self = this;
$.post('/save/url/', {the:data}, function (response) {
self.trigger('saveSuccessful', response);
});
}
});
When the savePost gets called it will be more difficult (without possibly introducing numerous hacks) to simply tell the view "Hey, the save was successful!".
Hopefully that's a bit more on target for what you were looking for.
yes, you can write but that file extension should be
".js.erb"

How to efficiently implement clientside javascript templating partials?

I'd like to build my own, but I'm not sure about the best way to do it. A partial is a template that is only a part of another bigger template and which can be inserted into multiple other templates at will.
Templating itself is fairly basic, just string exctraction and concatenation, but clientside partials have me a little stumped.
Here are a few methods I thought about:
1,
I could write a javascript helper function that loads partials through ajax into some form of local storage I suppose, and all subsequent templates that require that particular partial would first look inside local storage. I think this method isn't very safe because local storage isn't always guaranteed. And if I can't save them into local storage, partials would result in too many ajax calls.
2, I could put them all into script tags inside my main html file. This would work reasonably well, especially with head.js (to enable parallel loading of script tags), but still - I think each script tag is a separate call to the server right? That doesn't exactly improve the situation.
3, I could put all templates into a single script tag (or html I guess) and manually filter through some kind of delimiter...like: "#template1(blabla template1 string) #template2(blablabla template2 string) and put those strings into globals.
This would result only into a single call to the server, all the rest is done on the client.
Suggestions? I have looked at existing templating engines, but I can't really determine how they do it. The code is pretty complicated
The approach I took to spec out template calls and on-demand loads for the spec/rewrite of jQuery templates is to pipeline it.
See section 9 of the (early) draft spec, and see the conformance suite tests at the bottom for an example of custom on-demand template loading (Testcase "Main calls and Loaded just in time!" is the relevant one).
The basic gist is that plugin loaders (written in JS) get to hook in between-parsing and compiling to inspect the parse tree. A plugin pass gets an object mapping template names to parse trees. If they see any partial template selectors (to use your parlance) they can try and load any unresolved templates using AJAX calls or file I/O on Node.js, and add the partials to the input object to cause the compiler to compile the just loaded partials along with the public templates.
Efficiency-wise, see the benchmarks. I'm in the process of migrating the code to github : https://github.com/mikesamuel/jquery-jquery-tmpl-proposal in case you want to collaborate.

What is the best way to organize JS code in webapps where the main "meat" is still server side?

When building webapps with MVC web framworks like Django, Kohana, Rails and the like, I put together the application without JS-driven components initially, and then add them afterwards as "improvements" to the UI.
This approach leads to non-intrusive JS, but I don't have a good "standard" way of how to go about organizing the JS work. Most of the JS I write in apps like these are 10-30 line JQuery snippets that hook into some very specific part of the UI.
So far I often end up inlining these things together with the part of the UI they manage. This makes me feel dirty, I'd like to keep the JS code as organized as the python / php / ruby code, I'd like for it to be testable and I'd like for it to be reusable.
What is the best way to go about organizing JS code in a setup like this, where we're not building a full-blown JS client app, and the main meat is still server side?
I am also very interested in what other people have to say about this. The approach I've taken is to use object literal notation to store the bulk of the function, and store these in one file included on all pages (the library)
uiHelper = {
inputDefault:function(defaulttext){
// function to swap default text into input elements
},
loadSubSection:function(url){
// loads new page using ajax instead of refreshing page
},
makeSortable:function(){
// apply jQuery UI sortable properties to list and remove non javascript controls
}
}
Then I include a .js file on any page that needs to use the library that ties the elements on that page to the function in the library. I've tried to make each function as reuseable as possible and sometimes the event binding function on the page calls several of my library functions.
$(document).ready(function(){
$('#mybutton').live('click',uiHelper.loadSubSection);
//more complicated helper
$('#myotherbutton').live('click',function(){
uiHelper.doThisThing;
uiHelper.andThisThing;
});
});
edit: using jsDoc http://jsdoc.sourceforge.net/ notation for commenting for these functions can produce documentation for the 'library' and helps keep your code easy to read (functions split by comments).
The following question is along similar lines to your own - you should check it out...
Commonly accepted best practices around code organization in JavaScript
When dealing with JS code, you should first analyze whether it will be used right away when the page loads. If it's not used right away (meaning the user must do something to invoke it) you should package this into a JS file and include it later so the load time is perceived faster for the user. This means that anything that the user will sees should go first and JS related to the functionality should be imported near the end of the file.
Download this tool to analyze your website: http://getfirebug.com/
If the JS code is small enough, it should just be inline with the HTML.
Hope that helps a bit.
For quick little user interface things like that I put everything into a single javascript file that I include on every page. Then in the javascript file I check what exists on the page and run code accordingly. I might have this in UIMagic.js for example. I have jQuery, so excuse those jQuery-isms if they aren't familiar to you.
function setupMenuHover() {
if ($("li.menu").length) { // The page has a menu
$("li.menu").hover(function() { ... }, function() { ... });
}
}
$(setupMenuHover);
function setupFacebookWizbang() {
if (typeof FB != "undefined") { // The page has Facebook's Javascript API
...
}
}
$(setupFacebookWizbang);
I've found this to be a sane enough approach.
My preferred method is to store inline javascript in it's own file (so that I can edit it easily with syntax highlighting etc.), and then include it on the page by loading the contents directly:
'<script type="text/javascript">'+open('~/js/page-inline.js').read()+'</script>'
This may not perform well though, unless your templating library can cache this sort of thing.
With Django you might be able to just include the js file:
<script type="text/javascript">
{% include "js/page-inline.js" %}
</script>
Not sure if that caches the output.
If you are still worried about being 'dirty', then you could check out the following projects, which try to bridge the server/client side language mismatch:
http://pyjs.org/ (Python generating JavaScript)
http://code.google.com/webtoolkit/ (Java generating JavaScript)
http://nodejs.org/ (JavaScript all the way!)

Injecting a string into a javascript on an asp.net mvc view

I have a view that contains a javascript function that i need to init/call when the page loads. The method is static but it needs to be called with some parameteres (a list of Id's) - this list is dynamic and varies by some querystring parameters (i can not generate the list clientside from the querystring).
So i need to generate this list server-side.
My options are, as far as i see it:
1) Make an ajax call on the client, requesting the ids from the server.
2) Inject/inserting the ids directly on the view (it is a property on the viewmodel).
No matter how i turn it, option 2 seems the most sane. I already got the data ready on the viewmodel and thus it's ready when the view is populated - i see no reason to make an extra request to the server, just to get the data.
I know many would think it's a bad idea to inject something dynamic into an otherwise static javascript. For that i could simply just inject a new javascript, holding only the Ids and a call to the static javascript method, which is what i really want to do.
My problem is this though: When i write my asp.net <%= %> includes, VS IDE stops highligting, making me think i might be on the wrong track? Surely i'm not the only one needing to output something in a javascript block in asp.net mvc?
Route 2 (The ViewModel) is definately the way to go and
<script type="text/javascript>
<%= Model.JavascriptToInsert %>
</script>
should work (despite lack of VS highlighting)
It will NOT work in a seperate JS file though. It must be in your view itself.
Kindness,
Dan

Categories

Resources