The Phoenix web framework by default ships with every page including one js file: app.js. I am playing around with a web application which is more traditional in the sense that it's not a single page application. Beacuse of this, I didn't want a global js file, I thought the best way to go about it would be one global js file which everything requires, and then a few page-specific js files. This post explores how I went about it.


With help from Babel and Brunch, Phoenix compiles all js into one file: /priv/static/js/app.js by default. My goal was to have sibling js files in this directory that only get included on certain pages. I started by making a simple js file in /assets/js/pages/vote/vote.js. After this first step, brunch will find this file and compile it into app.js, however it will only be included if you require it in app.js. Inside this file, to make sure it was being included, I added a bit of code that would pop up an alert upon page load:

$(function() {
    alert('vote.js is loaded!');

Now I was set to play around with getting this alert to only display on particular pages.

Multiple js files with Brunch

We need our brunch-config.js to be a bit more explicit in the joinTo configuration. Instead of all javascripts being compiled into app.js, we want to use regex to match on certain directories and compile certain js source files into multiple js files. Here's the joinTo configuration which I'm using for this new vote.js we added in the last step.

joinTo: {
  "js/app.js": /^(?!js\/pages)/, // Will not grab anything from js/pages
  "js/vote.js": /^js\/pages\/vote/ // Only grabs the vote jses

Now app.js will be compiled from all javascript files except those found in js/pages/. Also vote.js will be compiled from all js files in js/pages/vote/.

Next, we need to autoRequire the appropriate module for our new compiled vote.js. Further down in the brunch-config.js file, modules.autoRequire looks like this:

autoRequire: {
  "js/app.js": ["js/app"],
  "js/entry.js": ["js/pages/entry/entry"],
  "js/competition.js": ["js/pages/competition/competition"]

Now you can test to see if your new vote.js file is doing what you'd expect. After doing this step, you should see your alert on every page. Add this line to your app layout (/lib/project_web/templates/layout/app.html.eex) after the script tag which includes app.js:

<script src="<%= static_path(@conn, "/js/vote.js") %>"></script>

You can now remove this line after seeing your alert.

View specific javascript includes

In my vote view (/lib/project_web/views/vote_view.ex) I added a function which returns the script tags that need to be added for every page which uses the vote view.

def render("includes.js", assigns) do
  entry_path = static_path(assigns.conn, "/js/vote.js")
  raw("<script src=\"#{entry_path}\"></script>")

This is an implementation of the render function with pattern matching on the page "includes.js". We've got to include this new script tag html in the main app layout (/lib/project_web/templates/layout/app.html.eex), so after the app.js script tag, add this line:

<%= render_existing view_module(@conn), "includes.js", assigns %>

Now all pages which use vote view should include the compiled vote.js file and should show an alert, but every other page shouldn't!