Search code examples
javascriptruby-on-railsturbolinks

Why shouldn't javascript be placed in view if it is only used on specific pages?


In his answer Richard Peck writes:

Unobtrusive JS

Something else to consider (you've already done this), is that you really need to use unobtrusive javascript in your application.

Unobtrusive JS basically means that you're able to abstract your "bindings" from your page to your Javascript files in the asset pipeline. There are several important reasons for this:

  • Your JS can be loaded on any page you want (it's DRY)

  • Your JS will reside in the "backend" of your app (won't pollute views)

  • You'll be able to use the JS to populate the various elements / objects you want on screen

It's always recommended you put your JS into separate files - including in the views sets you up for a big mess down the line

This beings me to the following question:

If I am only using a script on a certain page, why would I want to have it load on every page? Doesn't that go against the DRY? Maybe I am not properly understanding how the Rails pipeline works.


Solution

  • Rails pipeline

    It's not Rails pipeline, unobtrusive JS is a standard programming pattern.

    Extracting JS from the page ("inline") to an external file cleans up the page, nothing more.

    If you want to get the page loaded quickly, you need to split up the JS into separate files. This could be in the form of classes but is mostly for page-specific functionality. For example, you may have admin.js and application.js.


    Rails

    In regard to Rails specifically, the way to handle unobtrusive JS comes down to how you precompile your assets. The standard way is to put all the functionality into application.js - which will obviously become bloated.

    The way around this is to make use of the config.assets.precompile hook - allowing you to specify files you want to include as separately precompiled elements:

    # config/application.rb
    config.assets.precompile << %w(admin.js cart.js etc.js)
    

    This is going to change to being handled by manifest.js in Sprockets 4+ (I can explain this in an update if required). I made a commit to their repo about it.

    This means that when you precompile the assets (or when you run them in dev -- they're cached), you get admin.js or whatever you defined to be precompiled separately. This means nothing on its own; the files will just appear in public/assets.

    What it does mean is that you can then reference the files in your layout:

    # app/views/layouts/application.html.erb
    <%= javascript_include_tag :application, (:admin if [[condition]]) %>
    
    or
    
    # app/views/layouts/admin.html.erb
    <%= javascript_include_tag :admin %>
    

    Thus, you will be able to call the files you need when you need them.

    I can go into more depth but this should answer the immediate question.