One thing I really loved about Meteor, is that you can fine-tune which files are included (and the order of inclusion).
This is especially useful when structuring everything in small Meteor packages.
So for example, if you simply want to add relevant client JS files (on package.js):
1
2
3
4
api.addFiles([
    'client/page-login.html',
    'client/page-login.js',
], 'client');
page-login.html:
1
2
3
4
5
6
7
<template name="PageLogin">
  <form id="login-form">
    <input name="username" type="text" />
    <input name="password" type="password" />
    <input id="submit-login" type="submit" />
  </form>
</template>
page-login.js:
1
2
3
4
5
6
Template.PageNewDomain.events({
  'submit #login-form': function (event, template) {
    event.preventDefault();
    //handle form submission..
  }
});
Together with specific Template namespaces, it allows you to have template-specific JS code that doesn't interfere with other templates. I looked for a way to have that feature in Sails.js.

Instead of trying to implement the entire complicated Template rendering engine, I figured it should be enough if every view gets a corresponding JavaScript file that is optionally loaded with said view.

How it should work

  • Each View in the views/ folder has a name, let's create a corresponding JS file with the same name
  • On Sails.js, JavaScript files are stored separately in the js/ folder, let's store all view-specific JS files under js/views/
  • JS filenames should be the same as view filenames. views/dashboard/login.ejs will have a JS file named js/views/dashboard/login.js

How to implement

  • add this to config/views.js:
    1
    2
    3
    4
    
    locals: {
        existsSync: require('fs').existsSync,
        jsPath: __dirname + '/../assets/js/views/'
    }
    
    This is so that we're able to check if a JS file exists.
  • on the bottom of views/layout.ejs, under blocks.localScripts
    1
    2
    3
    
    <% if (existsSync(jsPath + req.options.controller + '/' + req.options.action + '.js')) { %>
      <script src="/js/views/<%= req.options.controller + '/' + req.options.action %>.js"></script>
    <% } %>
    
  • In tasks/pipeline.js in the jsFilesToInject array, we need to remove
    js/**/*.js
    This is Critical. Otherwise, all JS files under js/views will be automatically included in ALL views.
    Instead, to automatically inject other views we can have the following line:
    js/dependencies/**/*.js
That's it! Now you'll be able to write view-specific JavaScript files!