I have a WYSIWYG editor that I have built into the site and customised. There are a lot of Javascript files that only need to be loaded on the pages with the WYSIWYG editor; cur
This is getting old now and we're in a Webpacker world with Rails 6, but if you want to keep things more simple in the old school Sprockets way, you might like the approach described below. Note that it really is per-view - other answers have good approaches for wider scope per-controller stuff.
Have your main layout declare a 'content for' section. For example, in application.htm.erb
:
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
...etc...
<%= content_for :head %>
...
Here, :head
is just a label and you can give it any name you want. You can have as many such declarations in your layout as you want, too. They're just Rails's way of letting views insert extra stuff into those bits of your outer layout. So - you use this from within individual views to add your JS file(s) tags inside the
section of the layout.
For example, suppose I have manually "installed" the zxcvbn.js library by copying its one JavaScript file into vendor/assets/javascripts/zxcvbn.js
. I have an edit.html.erb
page where this is required, so at the top of that file, I add:
<% content_for :head do %>
<%= javascript_include_tag('zxcvbn', 'data-turbolinks-track': 'reload') %>
<% end %>
...removing the Turbolinks attribute if you're not using it. This means that when ERB compiles the page, it'll substitute that tag inside the 'content for head' part of the layout, so the script tag ends up in its place. This will of course mean an extra HTTP fetch when the page loads, but only in the views where it is used. In the above example the JS library is pretty big and would usually only used for one or two places related to users changing passwords; so this is a big win over having it lumped into a compiled application.js
and served everywhere, even though it's almost never used.
The content_for
stuff is quite clever by virtue of being quite simple under the hood. If your view was built from several partials, with more than one of them making the declarations, they don't overwrite each other. Each just gets concatenated into the right place, so the end result is pretty much what you'd expect without any nasty surprises.
There's one more step you need to avoid an exception from Sprockets because the asset you're trying to include isn't precompiled. You need to tell Sprockets it exists; for some reason, that's not automatically determined. In config/initializers/assets.rb
, declare "out-of-band" / unknown files that aren't otherwise included in the e.g. application.js
manifest file:
Rails.application.config.assets.precompile += %w( zxcvbn.js )
The out-of-box Rails-generated assets.rb
has comments explaining this and a commented-out example in place.
This is all cooperating normally with the asset pipeline, so it works as well (or badly, depending on your experience!) as anything else in the pipeline, with debug versions in development mode and minified content in production (subject to your pipeline configuration).