javascriptjqueryruby-on-railsruby-on-rails-5asset-pipeline

Efficient way to manage javascript/coffeescript code for Rails 5?


I'm building a web application in Rails 5.0.2. I have following JS files for my project:

enter image description here

Moreover, I have similar pattern for each of my own JS files as given below:

$(function () {
    var init = function () {
        // my code, I want to run on specific page load i.e. /remarks
        $('form#cf_remarks_form').validate(validate_options);
    };
    init();
    document.addEventListener("turbolinks:load", function () {
        init();
    });
});

I have following reservations/question on using JS assets:

  1. Rails by default attaches (and as a result they run) all js files on all pages for same layout. Is it good to add only relevant js on specific resources/pages by using customized pattern or go with default?

  2. If all js combine and linked to every page, I need to be very specific/cautious while using jQuery selectors as selection $('div.custom input.no-search') for one page might run me crazy if I have a lot of code and qualifying tag in another page on which I don't want to run a method, mentioned selector is selecting.

  3. Rails 5 uses ajax loading for normal pages. If have a file company.js for page /company, it will be executed on first page say /home. When I'll go to /company, lines written in company.js will not execute again. So, I have to use turbolinks:load event listener and encapsulate every UI initializing and UI attached events in a method to be called at turbolinks:load as well as normal call. (As shown in code snippet)

So, I want to know what practice rails developers follow for js assets specially who are using Rails 5? It will be great help if your answer address my concerns given above.


Solution

  • In answer to your questions:

    1. It depends. I would recommend trying the default first - it just requires that you be disciplined about selectors and classnames. However if you find yourself including huge amounts of js on every page that is only used on one backend page say, see the other possibilities below.
    2. Yes just use classnames which are specific to the resource you are targeting, like .remarks_form, and the js in each function can just bail out early if that classname is not found on the page (so you know the page isn't relevant).
    3. You should write your js to fire only once the page has loaded, that way it fires once for every page load - no need for two init() calls, just always fire when the document is loaded, or in turbolinks case on turbolinks:load. This is actually good practice even if not using turbolinks or rails, as you want the page to be loaded and initialised before js starts firing.

    You should be able to use this with rails 5:

    document.addEventListener("turbolinks:load", function() {
        console.log('Loads each time');
    });
    

    See the docs:

    http://guides.rubyonrails.org/working_with_javascript_in_rails.html#turbolinks

    There is a tradeoff here. You can include page-specific js either inline or in lots of separate files, but then the browser has to make another request for it, and probably won't have it in cache. Rails has chosen to keep things simple and assume that having one file which is cached for every page is better than 50 different js files across the site. YMMV but this does seem to work well, so I'd try the Rails way first.

    You can work around the Rails setup in various ways though if you find it inconvenient. I'd recommend keeping most of your js in one group, but you can also (in order of complexity):

    1. Keep most js in the main file but put some js inline on a page where it is only relevant to that page
    2. Import page-specific static js files in the header of a page (for example js for a text editor component only on backend screens).
    3. Split your js into asset groups and import some groups only on certain pages - see this answer for details.