Moving unobtrusive javascript out of html.erb file - ruby-on-rails

I have a html.erb file in one of my views. The file is pretty big and has about 2/3 javascript code and just 1/3 html. I dont like this file being too cluttered. The javascript part is mostly event handlers and usage of jquery UI components like date pickers, dialogs etc for the corresponding html elements in the page. I would like to separate the javascript part from the html part.
Can I move the javascript to a separate js.erb file and use it like a partial? What is the advantage of this?
How would I move the javascript to a static .js file when I use rails API like I18n.() and <%= form_authenticity_token %>. Should I pass them every time to the wrapping javascript function? Is there a better way to do this?
If I move javascript out of the html.erb file will it help in caching/rendering of html.erb page?
Interested to find out if there are any re-usable patterns
Sample code on the html.erb file:
<% content_for :javascript do %>
<script type="text/javascript">
$(document).ready(function()
{
$('#create_segment_dialog').dialog({
title: "<%= I18n.t("segment.create_segment_title") %>",
// lots of javascript
}
</script>
<%end %>
//HTML starts here
<div id="right-column">

My 2c, but I know there are differing opinions on this:
Try and separate out as much of your JS code into a function or functions and put them into JS files in your asset pipeline. Take advantage of the move to break your JS into re-usable components that could be used by other methods/controllers.
This means you get all the benefits of the asset pipeline for the JS you move there:
Caching & fingerprinting your JS assets
Minification & compression support to save bandwidth
Minification to obfuscate your code if that is something you want
Concatenation of JS files to reduce the number of requests a browser has to make
Possibility to serve the assets from another location (CDN, web tier vs app tier)
Improved DRYness if that JavaScript is used by other methods/controllers
The drawback? As you've pointed out, any Rails variables have to be passed in as parameters to the functions.
Moving your JavaScript to a .js.erb partial is an option, but if this code is very specific to a particular method or controller then it is not necessarily improving DRYness - it may make your code a little more readable, by separating HTML and JS.

Related

Using Asset Pipeline in Rails View

I have a Coffeescript view, something like widget.js.coffee which needs to include jQuery, as I can't be sure that jQuery is available. This idea is for other people to use the JS file, e.g.
<script src="http://my.rails.app/40/widget.js"></script>
My app already has jQuery via the asset pipeline, so I want to do the equivalent of the application.coffee manifest, where I can simply say something like:
#= require jquery
So far, it looks like I can output jQuery via so:
<%= Rails.application.assets["jquery"].source %>
but this seems to break the Coffeescript code (looks like there are backticks in the jQuery source).
I'm not sure the best way to proceed. Any thoughts on the best way to do this?
Make a .coffee file in the asset pipeline that includes all the libraries you require. Let's call it "widget.js.coffee" and it lives in assets/javascripts
In your controller, pull the generated source like so:
code = Rails.application.assets['widget'].source
Compress it
#js_libraries = Uglifier.compile code
Use it in your view. If your view is coffeescrtipt, make sure it is enclosed in backticks
<%= raw #js_libraries %>

Is there an advantage to using link_to over the anchor tag in Rails?

Don't these two do the same thing?
<%= link_to "Example", '#', class: "somestyle" %>
Example
If I'm writing a static .html.erb page, if everything else is written with HTML tags, doesn't it make sense to use HTML tags for links as well? I'm not sure why one should use a helper. Similarly, for linking style sheets, javascripts, etc.
For the link tags, it may not make a difference which way you go. Unless you're linking to more than "#". For instance, using a routed path.
For the stylesheets and javascript, I think you will need to continue to use the Rails helpers if you're taking advantage of the asset pipeline. If so, the hash in the filename changes at each asset compilation (I believe), and manually trying to edit the filename each time could become a pain.

How does RequireJS work with multiple pages and partial views?

I'm looking into RequireJS but I'm uncertain about some things.
I understand how I can load all my dependencies with main.js.
But do I need to add any logic to work with those dependencies in main.js?
To me, main.js seems like a document.ready state and you enter logic there when the document has loaded, right?
And for other pages and partial views, do I need to create multiple main.js or can I just reference loaded functions in dependencies from the views in a <script> for example?
Update -
I've added an example of using RequireJS with modular HTML components. Build tool example included - https://github.com/simonsmith/modular-html-requirejs
I have also written a blog article about this - http://simonsmith.io/modular-html-components-with-requirejs/
The approach of just using main.js for everything is probably more suited to a single page application.
The way I've handled this situation is to only include common site-wide JS in the main.js file:
On every page:
<script src="require.js" data-main="main"></script>
main.js
require.config({
// config options
});
require(['jquery', 'common/ajaxLoader', 'common/someOtherModule'], function($, ajax, otherModule) {
// Modules that do stuff on every page are instantiated here
});
page1.html
<script>
require(['scripts/page1']);
</script>
page1.js
require(['jquery', 'page1Module'], function($, module){
// page1 specific stuff here
});
The above example is just one of a couple of ways to handle it. Note the difference between loading a plain JavaScript file and a module.
A rule of thumb I follow is to keep all reusable modules (or Classes if it makes it easier to conceptualise) inside a define with their own dependencies etc and then use require to grab those modules, use their methods or interact with them in some way.
Using this pattern will almost certainly require use of the domReady module that is a separate plugin for RequireJS. Use this instead of a ready function in jQuery for example, as it allows modules to begin downloading before the DOM is ready which reduces the wait for your code to execute.
Edit You may wish to see another example of multi-page application in the RequireJS repo
I have recently gone through the exercise of setting up RequrieJS with automatic build optimization in an ASP.NET MVC application. There are a lot of helpful blog articles such as Simon's that are a great reference. From an ASP.NET perspective one of the most useful I found in terms of configuring the RequireJS optimizer for multi-page ASP.NET applications was Making RequireJS play nice with ASP.NET MVC.
Using the great information already out there I have put up my own ASP.NET MVC RequireJS example on GitHub. Much of what is included is similar to examples already out there, however to address the issue of partial views, and multi-page require dependencies I have taken a slightly different approach.
_Layout.cshtml
The most notable difference from existing examples is the creation of a custom RequireViewPage that exposes methods to pass configuration data to RequrieJS as well as reference page specific require dependencies.
So your _Layout.cshtml will look much like what you'd expect with:
<head>
...
#RenderModuleConfig()
<script type="text/javascript" src="#Url.Script("vendor/require.js")" data-main="main"></script>
</head>
<body>
...
Views & Partials
To wire up views (and in my case knockout view models), an additional script fragment has been added to the bottom of _Layout.cshtml as follows
...
#RenderSection("scripts", required: false)
<script type="text/javascript">require(['main'], function () { require(['lib/knockout/knockout.require']); });</script>
</body>
This will ensure that for any view dependency, the main module has been loaded (assuming dependencies for main have being defined in main.js and then allows for view specific dependencies to be wired up via data attributes.
<div data-require="#MainModule"> ... </div>
<div data-require="#Module("address")"> ... </div>
<div data-require="view\home\index\model"> ... </div>
For a full explaination of the design and choices, see the README on GitHub

Where in the Rails framework should I place my Backbone templates?

I'm a rails developer trying to learn Backbone and then I ran into this problem: since Underscore templates include symbols like <%=%>, I guess templates can't be included into erb files, so is it okay to have a rails partial for every single template? And what extension should it be?
You can escape the erb symbols by using two % in the opening tag, and put your backbone templates in the rails views:
<script type='text/template' id="my-template'>
<%%= name %>
</script>
will output the following in your page:
<script type='text/template' id="my-template'>
<%= name %>
</script>
Putting your Backbone templates directly in your rails views is IMHO the best option when you're trying to learn. You're already wrestling with the new concepts, no need to add another hurdle.
Starting with Rails 3.1, it provides two things that make working with Backbone templates a little easier: the asset pipeline, and automatic JST (JavaScript Template) compilation.
Create a directory in your app/assets folder called templates. This directory will automatically be picked up by the asset pipeline.
Next, name the files in that directory with an extension of jst and the type of template you are creating ejs (embedded javascript). You can even nest them in directories. For example:
app/assets/templates/my_template.jst.ejs
app/assets/templates/bookmarks/show.jst.ejs
The asset pipeline also allows you to use other templating languages like embedded coffeescript, mustache, handlebars, etc. by simply changing the file extension (and including any necessary gems).
Now to reference your JST templates in your Backbone views, simply use the path to the filename:
var Bookmark = Backbone.View.extend({
template: JST['bookmarks/show'],
render: function() {
this.$el.html(this.template(this.model.attributes));
return this;
}
});
You may need to add this line to your application.js:
// require_tree ../templates
Here's a nice article which explains all of this in a little more detail: http://www.bigjason.com/blog/precompiled-javascript-templates-rails-3-1
Where should you put your Backbone templates? I'd say nowhere. I believe that in most Rails applications, the server should be responsible for all rendering of HTML, while the client-side JavaScript should just be responsible for inserting that rendered HTML into the DOM. Among other things, this makes I18n easier.
The exception would be if Rails is simply being used as a lightweight backend for an application that runs mostly on the client side (though in that case, you might want to use Sinatra or something instead). In this case, Rails should probably render nothing, and have the JS do all the rendering.
Notice the underlying principle here. Either the server should be responsible for all rendering, or the client should. Splitting it will make life harder.

No pics in my PDF created with PdfKit on heroku

I've an app which works fine in development and on my current production server.
I want to move it to FREE heroku (basic config: 1 dyno, 1 worker).
Unfortunately, the pdf generation (using PdfKit) is ok BUT without the pictures defined in my CSS.
I've followed a lot of tips including:
http://blog.mattgornick.com/using-pdfkit-on-heroku
http://jguimont.com/post/2627758108/pdfkit-and-its-middleware-on-heroku
http://code-fu.pl/blog/2011/05/17/pdfkit-heroku
Thoughts?
Found a workaround but I am still eager to know a better option:
I duplicated my view: one dedicated for html, another for pdf.
I removed all css using pics and put it in a separate file, included only in the view dedicated for html
finally, I inserted the css in the view dedicated to the pdf:
.foo { background-image:url(<%= Rails.root %>/public/images/bar.png) }
Very Ugly but works so please tell me if you've better
It's probably an issue with the way the url's are specified in the css. As I recall, they should be file system absolute paths. What does your css look like?
Here is how I answered my needs with:
Just one single view file
Just one css file
The trick was to pass the proper base_url to the css file dynamically, given I expected a pdf or html.
I decided to use LESS. Style compiles css in a different manner, given the base-url I provide in the DOM. This base-url is generated by a helper.
Here were my steps:
changed my style.css to style.less
Added to my view:
<%= stylesheet_link_tag "style.less", :rel => "stylesheet/less" %>
<script id="base_url" type="text/javascript" data="<%= assets_path %>"></script>
<%= javascript_include_tag "less.min.js" %>
In my helper:
def assets_path
if request.fullpath.include? ".pdf"
"#{Rails.root.join('public',"images","pictos")}"
else
"#{request.protocol}#{request.host_with_port}/images/pictos"
end
end
and in my style.less:
#base_url: `document.getElementById('base_url').getAttribute('data')`;
.foo { background-image:~"url(#{base_url}/bar.png)" }

Resources