14

For example, I have a page /locations/map which I need to include Google Map library, and include a .js file (e.g. location.js) specifically for this page only.

I want to inject these 2 files to after <!--SCRIPTS END--> this line

Is it possible to do this?

NOTE: I was using Sails.js v0.10

Js Lim
  • 3,625
  • 6
  • 42
  • 80

5 Answers5

25

Sails uses ejs-locals in its view rendering, so you can accomplish what you want with blocks.

In your layout.ejs file, underneath the <!--SCRIPTS END-->, add (for example):

<%- blocks.localScripts %>

Then in the view you're serving at /locations/map, call the block with your script tag, for example:

<% block('localScripts', '<script src="https://maps.googleapis.com/maps/api/js"></script>') %>

As an alternative, you could put the <!--SCRIPTS--> and <!--SCRIPTS END--> tags in the <head> of your layout, and then add your view-specific scripts directly into your view rather than using blocks. This is a fine option if you don't mind waiting for those linked scripts to load before your page content is displayed.

sgress454
  • 24,870
  • 4
  • 74
  • 92
  • Wow~it works. but is it possible to inject in `MapController` rather than in view? – Js Lim Mar 04 '14 at 00:21
  • In your controller action, you could set `res.locals.gmapScript=' – sgress454 Mar 04 '14 at 00:27
  • That means no matter how, `<%- block('localScripts', gmapScript) %>` this line must put in view? – Js Lim Mar 04 '14 at 00:29
  • Yes, the only way to add text to your view (besides using the linker, i.e. the `` tag) is by putting it in the template. The benefit of this is that you always open that view file and have an idea of what's in there. If you started adding text to the view in your controller before sending a response, it would be a lot harder to debug! – sgress454 Mar 04 '14 at 00:36
  • The initial solution (<%- blocks.localScripts %>) works but everything I place in the block appears two times - Once where it is declared in the view and the other time is in the layout where blocks.localScripts is defined. Should this happen? (I solved it by adding the block in the view in html comments) – prototype May 11 '15 at 19:50
  • It should be `<% block(...` instead of `<%- block(...` to prevent populate the contents twice. Full sample: `<% block('localScripts', '') %>` – James Chen May 02 '16 at 13:02
13

Scott's answer is the proper way to insert non-global JS into a specific view. Just a little comment, though: the block call from the view should not have the dash. It should be as follows:

<% block('localScripts', '<script src="https://maps.googleapis.com/maps/api/js"></script>') %>

Both calls will work, but using the dash makes the insertion twice; once the view is loaded and previous to the layout render, and then once again when the view is inserted in the rendered base layout. This leads not only to inserting/running unnecessarily twice the same code but also to errors that break your JS code if the inserted script depends on libraries that you have in your base layout (e.g. jQuery, Backbone).

EJS interprets the magic <%- as "insert unescaped". So, -I guess- what this is doing is calling the block() function, which returns our HTML <script> tag. This is replaced where the magic was called but also is executing the block() function inside of it, which is executing the layout block localScripts replacement.

On the other hand, <% means "instruction". I.e., just run this JS piece of code, which is not echoed to the view where is called.

Federico Freire
  • 155
  • 2
  • 6
4

I discover other way to do that

In MapController.js

// you can add as many as you like
res.locals.scripts = [
  '//maps.googleapis.com/maps/api/js',
];
return res.view();

In layout.ejs

<!--SCRIPTS-->
<!--SCRIPTS END-->

<!-- Loop through all scripts that passed from controller -->
<% if (scripts) { %>
    <% for (i = 0; i < scripts.length; i++) { %>
        <script src="<%- scripts[i] %>"></script>
    <% } %>
<% } %>
Js Lim
  • 3,625
  • 6
  • 42
  • 80
  • I think this might be the solution Im after to solve this: http://stackoverflow.com/questions/27120430/inject-javascript-to-specific-view-route-in-sailsjs-after-all-the-global-javascr/27121109?noredirect=1#comment42743390_27121109. May I ask do we have a way to populate the scripts even nicer? – hjbolide Nov 25 '14 at 08:13
  • @hjbolide My sailsjs project has been suspended, if you found another way to make it nicer, go ahead. I'm now doing more ios project, can't help you much in this – Js Lim Dec 02 '14 at 10:23
  • 1
    it's very very appreciated, and my project is now using your solution, I love it ! Thanks for sharing. – hjbolide Dec 04 '14 at 02:53
3

This method allows flexibility to locally serve js files from any page and also prevent any reference errors caused by dependencies.

In pipeline.js insert '!js/local/*.js at the bottom of jsFilesToInject like so:

var jsFilesToInject = [

// Load sails.io before everything else
'js/dependencies/sails.io.js',

// Dependencies like jQuery, or Angular are brought in here
'js/dependencies/jquery-3.3.1.min.js',
'js/dependencies/**/*.js',

// All of the rest of your client-side js files
// will be injected here in no particular order.
'js/**/*.js',

//Ignore local injected scripts
'!js/local/*.js' 

];

Create a local folder inside the /assets/js folder ie /assets/js/local/. Place any locally injected scripts in here.

In your master view ejs ie layout.ejs insert <%- blocks.localScripts %> below the SCRIPTS block like this:

<!--SCRIPTS-->
<script src="/js/dependencies/sails.io.js"></script>
<script src="/js/dependencies/jquery-3.3.1.min.js"></script>
<script src="/js/dependencies/bootstrap.min.js"></script>
<script src="/js/dependencies/popper.min.js"></script>
<!--SCRIPTS END--> 
<%- blocks.localScripts %>

In your local ejs view (eg. homepage.ejs) insert your localScripts block like this:

<% block('localScripts', '<script src="/js/local/homepage.js"></script>') %>

sails v0.12.14

EDIT Is this still relevant for Sails v1.0?

My answer is a resounding YES and in my earlier answer I lacked explaining how to get the most out of the Grunt pipeline like clean, coffee, concat, uglify etc... when going into production.

The trick here is to make a local file (there should only be one per page) as small as possible.

  • Group and name specific your function calls
  • Save functions as separate files for easy maintenance and group them into folders.
  • Group bindings and any initialising of global variables into a couple of functions like initThisPageVariables() and initThisPageBindings() so that Grunt can crunch these later.
  • Set a master function call to run your app startThisPageApp()

Then simply calling the few functions from your local (master) file to get things rolling.

$(window).on('load', function(){

   initThisPageVariables();
   initThisPageBindings();

   $(window).on("resize", function(){
      initThisPageVariables();
   }).resize();

   startThisPageApp(); 

});
Eli Peters
  • 406
  • 4
  • 10
  • Took me a while to understand the logic,but it is genius : "'!js/local/*.js' " stops sails from loading js from this location. <%- blocks.localScripts %> loads code, pointing to the local script if told so. <% block('localScripts', '') %> tells which file should be loaded. Thank you for your answer!!! – deckoff Nov 09 '18 at 22:55
0

I know this is an old question by I discovered another option.

File:

config\routes.js

Code:

'/': 'HomeController.StartHome'

I set StartHome function in HomeController responsible for managing '/' route.

File:

api\controllers\HomeController.js

Code:

StartHome: function(req, res) {

    var template_data = {
        "data" : {
            "view_name" : "home_view"
        }

    }

    res.view('home_view', template_data)
}

Here, I created an object with some data which is passed to EJS template(to client).

File:

views\layout.ejs

Code:

<% if (data["view_name"] === "home_view") { %>
<script src="script_for_home_view.js"></script>
<% } %>

In my layouts.ejs I created if statement which "enables" script tag depending on on the view I am currently on.

This is how I handle this. I hope it's clear for you.

Sails 0.12.4

Daniel
  • 338
  • 3
  • 13