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.
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 https://localhost:1337. When you get there, you should see something like this:
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="https://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:
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="https://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:
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!
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?
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.
Ah okay, so it’s just a placeholder until you get to Angular. Thanks for the explanation. Looking forward to the next part.
Thanks! I’m glad you’re enjoying it 🙂
Hi
There’s no app when I add “require(‘express-helpers’)(sails.express.app);”:
Thanks!
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);
Great tutorial! When will part 2 & 3 be out?
Part 2 is already published here: https://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.
awesome, thanks! keep up the great work
Please, update your article for Sails version 10.5
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,
}
…..
}