Creating An Application With Sails.js, Angular.js and Require.js Part 1 – Your first server-side view

by Tyson Cadenhead on July 29, 2014

The modern web is always changing, and this article is more than two years old.

Tyson Cadenhead begins an article series about building an application with Sails.js, Angular.js and Require.js. Sails.js is a Node.js framework built to make it easy to create enterprise grade Node.js apps.

By Tyson Cadenhead

Sails JS is a really cool framework for building realtime web applications. Today I am going to show you how easy it is to create a new Sails project. In many ways, Sails is similar to Ruby On Rails. It allows you to create a new application and sets enough defaults that you can run it right away.

Install Sails

Let’s start by installing Sails. You will need to have Node.js installed too, but hopefully you’ve already got Node running at this point. To install Sails with npm, run this in your terminal:

sudo npm -g install sails

Create Your Project

Sails comes with a bunch of command-line tools. The new tool will help us create a new application:

sails new todo

Awesome! You just created an app! Now you just need to enter into the folder that the app went into and start the sails server:

cd todo
sails lift

Very good. Now if you go to http://localhost:1337. When you get there, you should see something like this:

sails1

That is basically all there is to creating a new Sails project. If you want to follow along, I’m making a git repository to show the application we’re building. We should currently be at this point in the process of building this out.

Sails Server-Side Views

Next, we are going to look at how Sails.js handles server-side templating. Sails lets you use whatever type of templating you want want, but out of the box, it uses EJS, which is based off of Ruby’s ERB.

Lets start by going into our project directory and starting our server using:

sails lift

All of the views will be served from the /views directory. If you go to localhost:1337, you can see that there is already a template that comes with Sails. That template can be found in views/home/index.ejs. The individual pages are all wrapped with a layout template as well. The layout is located in /views/layout.ejs.

The Layout

Let’s replace the layout with our own markup:

<!DOCTYPE html>
<html>
  <head>
    <title><%- title %> | TODOIT</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css">
    <link rel="stylesheet" href="/styles/app.css">
  </head>

  <body>
    <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#"><i class="fa fa-check"></i> TODOIT</a>
        </div>
        <div class="collapse navbar-collapse">
          <ul class="nav navbar-nav">
            <li class="active"><a href="/">Home</a></li>
            <li><a href="/about">About</a></li>
            <li><a href="/list">My List</a></li>
          </ul>
        </div>
      </div>
    </div>

    <div class="container">
        <div class="row featurette">
            <div class="col-md-7">
              <h2 class="featurette-heading"><%- title %></h2>
              <%- body %>
            </div>
        </div>
    </div>

    <script type="text/javascript" src="/js/socket.io.js"></script>
    <script type="text/javascript" src="/js/sails.io.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
    <script type="text/javascript" src="/js/app.js"></script>
  </body>
</html>

You may notice that we aren’t using RequireJS to load our modules yet. Don’t worry, we’ll be covering that in the future. You may also notice that we are referencing a styles/app.css file, we will need to create that:

body {
  padding-top: 50px;
}
.starter-template {
  padding: 40px 15px;
  text-align: center;
}

You will need to restart your server to see the stylesheet show up. I know it is annoying to have to restart your server when you change a style or script, but we’ll be looking at a solution for that soon.

Home Page

Now let’s replace the markup in the home/index.ejs file:

<p class="lead">Check it out. Check it off.</p>
<img src="http://cdn2.content.compendiumblog.com/uploads/user/4268c6b6-ce2f-102a-80e3-0015c5f70ec2/aa4211a4-bb44-46d7-8e55-e2bf85cbb4a4/Image/01056b67869d6ad5848d9df376132aa5/questions_to_ask.jpg" />

Passing Data To Your Views

You might notice that in the layout template, we are referencing a <%- title %> attribute. By default, the title will be “Sails”. You can override that globally by creating a config file like this one I made in config/app.js and adding your own appName:

module.exports = {
    appName: 'TODOIT'
};

If you want a different title on each page, you can also control the title from your controller. If you look at your config/routes.js file, you will see that the / route is currently just rendering a view instead of being driven by a controller. Why don’t we generate a controller to drive the home page?

sails generate controller home

The command above will generate a new empty controller in your api/controllers directory called HomeController.js. We can add a method to the module.exports called “index.”

index: function (req, res) {
    res.view(null, {
        title: 'Home'
    });
},
...

The first argument in the res.view() method is the template name. If it is a string, a template with that name will be rendered. If it is blank, the controller uses the controller name and the method name to create the path to the view. The second argument is an object of data that the controller is passing into the view. That is where we can add the title.

Now we will need to update the routes to use the home controller instead of just rendering the home view with no controller.

Find this line:

'/': {
    view: 'home/index'
}

and change it to:

'/': {
    controller: 'HomeController',
    action: 'index'
}

Once you restart your server and go to the home page, you should see the title “Home” displaying correctly. It should look like this:

sails-server-views1

About Page

Now, let’s go ahead and create an about page:

sails generate controller about

In the module.exports for api/controllers/AboutController.js, add this method:

index: function (req, res) {
    res.view(null, {
        title: 'About'
    });
},
...

Now open your config/routes.js file and add a this route underneath the previously created home page route:

'/about': {
    controller: 'AboutController',
    action: 'index'
}

Last of all, we will need to create the view for our about page. We will create a file name /views/about/index.ejs with this markup:

<p class="lead">This is an example application to show how to use Sails.js with Angular. If you would like to follow along with how it was created, check out this <a href="http://www.tysoncadenhead.com/blog/sails-ninja-intro">blog series</a> that walks you through step by step.</p>

Now, when you go to localhost:1337/about, you should see this:

sails-server-views2

URL Helpers

Sails ships with just the standard EJS implementation. There are some handy helpers that Sails doesn’t take advantage of. Luckily, it’s easy to add them to your app.

First, just install the dependency:

npm install express-helpers --save

and then add this to your config/bootstrap.js file:

require('express-helpers')(sails.express.app);

With that update in place, we can change the layout.ejs file to use the link_to() helper like this:

<ul class="nav navbar-nav">
  <li class="active">
    <%- link_to('Home', '/') %>
  </li>
  <li>
    <%- link_to('About', '/about') %>
  </li>
</ul>

When you restart your server and refresh the app, it should still display correctly, but it is using the view helpers now.

Active Section

Because the controllers are already sending the title to the views, we can use that to determine what page we are on. We’ll apply an “active” class to the page name in the navigation that we are currently on:

<ul class="nav navbar-nav">
  <li class="<% if (title === 'Home') { %>active<% } %>">
    <%- link_to('Home', '/') %>
  </li>
  <li class="<% if (title === 'About') { %>active<% } %>">
    <%- link_to('About', '/about') %>
  </li>
</ul>

That is the quick rundown of Sails templates and how they work. If you want to see all of this code together, check out the repository at this point.

This article is a mashup of two articles Tyson originally posted on his blog.

Watch for the next article in this series which will be posted next week!

12 comments"

  1. Ben Black says:

    Thanks for the helpful article! I was just about to start a Sails+Angular app. Could you tell me why you’re using EJS templates when Angular will soon be substituted in?

    1. tysoncadenhead says:

      Sails uses the EJS templates out of the box. This particular example is not a single-page application, so I needed to use some sort of templating system to render the server-side views, which is why I was using the EJS templates. Of course, If it was a single-page application, there would be very little use for server-side templates at all.

      1. Ben Black says:

        Ah okay, so it’s just a placeholder until you get to Angular. Thanks for the explanation. Looking forward to the next part.

      2. tysoncadenhead says:

        Thanks! I’m glad you’re enjoying it 🙂

  2. Asaf says:

    Hi

    There’s no app when I add “require(‘express-helpers’)(sails.express.app);”:

    error: Bootstrap encountered an error: (see below)
    error: TypeError: Cannot read property ‘app’ of undefined

    Thanks!

    1. Elie GAKUBA says:

      Hi Asaf,

      Since the release of 0.10.0rc5 it looks like they have been a major refactoring to the new sails hook API. use this call instead
      require(‘express-helpers’)(sails.hooks.http.app);

  3. Phil says:

    Great tutorial! When will part 2 & 3 be out?

  4. Part 2 is already published here: http://modernweb.com/2014/08/05/create-an-app-sails-angular-require-pt-2/

    We will be publishing the next parts once a week going forward.

    1. Phil says:

      awesome, thanks! keep up the great work

  5. Niklolay says:

    Please, update your article for Sails version 10.5

  6. Keyner TYC says:

    It works for me:

    Sailsjs v. 0.11.0

    config/http.js

    var helpers = require(‘express-helpers’)();

    module.exports.http = {
    …..
    locals: {
    “date_tag” : helpers.date_tag,
    “form_tag” : helpers.form_tag,
    “form_tag_end” : helpers.form_tag_end,
    “hidden_field_tag” : helpers.hidden_field_tag,
    “input_field_tag” : helpers.input_field_tag,
    “link_to” : helpers.link_to,
    “submit_link_to” : helpers.submit_link_to,
    “link_to_if”: helpers.link_to_if,
    “link_to_unless” : helpers.link_to_unless,
    “password_field_tag” : helpers.password_field_tag,
    “select_tag” : helpers.select_tag,
    “single_tag_for” : helpers.single_tag_for,
    “start_tag_for” : helpers.start_tag_for,
    “submit_tag” : helpers.submit_tag,
    “tag” : helpers.tag,
    “tag_end” : helpers.tag_end,
    “text_area_tag” : helpers.text_area_tag,
    “text_tag” : helpers.text_tag,
    “text_field_tag” : helpers.text_field_tag,
    “url_for” : helpers.url_for,
    “img_tag” : helpers.img_tag,
    }
    …..
    }

Leave a Reply

Your email address will not be published. Required fields are marked *