Follow me on twitter here.
Subscribe to the sailscasts mailing list here.
The repo for this episode can be found here.
Transcript
In activityOverlord v1.0 the user interface or Views
of the app were built primarily on the server before being sent to the client and rendered by the client’s browser. This approach is the V
of the MVC architecture and also known as Server Rendering even though the web page is actually being rendered on the client’s browser.
This so called Server Rendering is the server preprocessing one or more templates of markup, usually some combination of HTML, CSS, and Javascript and combining them with data via a Template Engine. All template engines use some form of tags that surround variables that when processed yield a result. Sails uses the EJS template engine by default. For example, this template contains some standard HTML mark-up along with EJS
tags.
1 2 3 4 5 6 |
|
Between the tags is a variable title
. When the Sails server processes this template it will attempt to replace the title
variable with a value, hopefully the page title. This is also called String Interpolation, but I only mention that because I’m a geek.
Server Rendered Views
So let’s review this approach. When a client browser makes a request
to the server, the router
parses the request
and determines where to send it. Now, the resulting route
typically points to a controller/action
that might execute some logic. For example, that logic might include accessing a mongo database that retrieves a stored twitter id and access token. The id and token are then used to request additional details of the user from the Twitter API, all before the server pre-processes a template which contains tags with variables that are replaced by the Twitter details into a View
. The View
is then sent back to the client and ultimately rendered by the browser.
Now, this traditional approach to web applications has at least two weaknesses. First, its reliance on the server for page creation means the responsiveness of the app is impeded by the constant round trips necessary to update changes to the View
from the server. Second, is that the API being tightly coupled to the View
makes it less flexible for other potential consumers to use.
A more modern approach to web applications solves both weaknesses by pushing the responsibility for changing the UI to the client as well as decoupling the API to act as an independent provider of endpoints. These endpoints can then be accessed by the browser UI, a native app UI, a mobile UI, or even a smart refrigerator UI.
Now this doesn’t mean that we’ll be abandoning Server Rendered Views entirely. Instead activityOverlordv2.0 will take a hybrid approach using a blend of Server Rendered Views combined with Front-end Framework Components to deliver a UI that makes authentication and SEO manageable. But as usual I’m getting ahead of myself.
So let’s go back to the project and see what Server Rendered Views look like in action. By default, Sails generated three files — homepage.ejs
, layout.ejs
, and a route to the homepage contained in /config/route.js
. So when I make a request in the browser to localhost:1337, the Sails router looks at the request and matches it with the route
in the routes file (e.g. route.js
). This triggers the View Engine to pre-process layout.ejs
with homepage.ejs
to produce the View that’s being rendered by my browser. The Layout.ejs
file is actually a wrapper around homepage.ejs
. And what I mean by that is if we look at both files you can see that layout.ejs
contains typical mark-up that we want for every page, things like DOCTYPE, html, and head tags. So we have EJS tags here with a body
variable which when processed by the Sails View Engine is replaced with the contents of homepage.ejs
. The result is our View.
Despite it’s power, and to make this example crystal clear, I’m going to disable the layout functionality in Views at least for the time being. To do this I’ll go into into /config/views.js and change the layout
parameter from layout
to false.
The first part of activityOverlord v2.0 consists of the Signup Page and when completed will look something like this. Let’s get started. I’ll first create a new file named startup.ejs
and I’ll put a simple header in it <h1> Signup Page</h1>
. Next, I’ll remove the existing route
to the homepage
and replace it with a route
to the new The Signup Page
:
1
|
|
Let’s take a look. I’ll start Sails using sails lift
and then navigate my browser to localhost:1337/signup
. Okay good, here’s our signup page.
Let’s go back to the Signup Page
and I’ll start by copying in some boilerplate html
. Next, I’ll add our first Angular directives
into the body tag — ng-app
, ng-controller
, and ng-cloak
. Let’s refresh the browser and see what happens. Well nothing happens, because we haven’t yet added Angular yet. We could manually add the script tags that reference Angular, however, Sails can do this automatically for us using it’s own tagging system (e.g. <!--SCRIPTS-->
tag). You know what, it’s easier just to show you. So I’ll add the tags to signup.ejs
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
I’ll restart the Sails server using Sails lift
and then go back into the browser but also open up the browser console…okay let’s refresh the page and see what happens. Okay, that new, we get this console message that we’re “connected to Sails”, but where did that come from? If we look at the page source we’ll get a clue. Towards the bottom of the page a link to a file namedsails.io.js
has been added to our signup page (e.g. signup.ejs
). But where did that link come from? Let’s go into our project in Sublime and navigate to assets/js
. In the dependencies foler there’s our sails.io.js file. First, this file deals with web sockets
and socket.io
, which we’ll be covering in later episodes. The big question now is, how did a link to that file get inserted into signup.ejs
? The short answer is that Grunt did it for us. Okay, so what’s Grunt
?
Grunt
calls itself a JavaScript Task Runner but really Grunt
is all about automation. It allows you to create repetive tasks that can be executed automatically. For example, there’s a Grunt task that’s looking for changes in the Sails assets folder. Sails creates a .tmp folder in the root of our project. As we can see here any files in the assets folder are copied into .tmp/public which acts like a static file folder you’d find on any web server. But what about the link to sails.io.js? This is yet another Grunt Task that will place a link to any javascript file found in assets/js that has the corresponding Script tags we just placed in signup.ejs (e.g. <!--SCRIPTS--><!--SCRIPTS END-->
).
Of course all of these tasks are happening automatically without us having to be aware of them. However, for those of you who want a little more detail behind the magic, I created a doc that goes into much more specifics of the Grunt/Sails integration than I do in this screencast. The documentation can be found here.
So let’s go ahead and place the main Angular file in the assets/js/dependencies
folder and I’ll refresh the browser. Looking at the page source we can see that a link to angular has been automatically placed in our signup page. But when I refreshed the browser, Angular is mad at us because we have some directives in the signup page without any associated javascript files. But not to worry, we can easily fix that. Let’s navigate back to the assets/js
folder in Sublime and we’ll create a folder called public
. We’ll put all of our Angular files that have to do with those parts of the application that don’t require authentication. Within public
we’ll create a folder called signup
. And within signup
we’ll create two files SignupModule.jsand
SignupController.js. Okay, first let's take a look at
SignupModule.js` and I’ll add the bare bones Angular code to define our new module.
1
|
|
Next, I’ll open SignupController.js
and add the bare bones Angular code to define our new controller.
1
|
|
Let’s go back to the browser and refresh the page. As you can see, we still have an Angular issue. If we look at the page source, we can see what the issue is, SignupController
is getting loaded before SignupModule
. This is easy to fix and touches on another aspect of Grunt. Most of the configuration for Grunt can be found within the activityOverlord20/tasks
folder. For this issue we’re going to look in the root of the tasks
folder for a file named pipeline.js
. This file contains the configuration for how the SCRIPTS tags are used (e.g. <!--SCRIPTS-->
) as well as some other tags we’ll be using shortly.
Next, I’ll open pipeline.js
in Sublime. We want to load the SignupModule
after dependencies but before any other javascript files. So we’ll put the path to our file here.
1 2 3 4 5 6 7 8 9 10 11 |
|
Now when I refresh the browser, Angular no longer complains and if look back at the page source we can see that SignUpModule is being loaded before SignupController.
So let’s go back to signup.ejs in Sublime and insert the remainder of the markup for our Signup Page. Let’s start-up Sails using Sails lift
and navigate the browser to localhost:1337/signup. Although the page loads without errors, there’s some obvious dependencies that need to be added in order for this page to look right. First I’ll add Bootstrap (e.g. bootstrap.css
) to the /assets/sytles
folder. Similar to what we did with javascript files in the previous episode, Sails will automatically include links to the CSS in signup.ejs
. Taking a look at signup.ejs
we can see the Styles tags that were added when I brought in the earlier mark-up as well as a link Grunt automatically created to the bootstrap file I just added.
1 2 3 4 5 6 |
|
Next, I’m going to create a fonts
folder and add some fonts we’ll be using later in the interface.
I’m also going to add some .less
files into the assets/styles
folder that we’ll being using later in the interface as well.
You may have noticed the importer.less
file which let’s us control which less files are included the Styles Tag as well as their order. Note that the Grunt task included in Sails will only compile the .less
files that are referenced in this file.
I’m also be adding Jesús Rodríguez’s https://github.com/Foxandxss fantastic angular messaging library Angular-toastr. The library contains both a javascript and a css file.
Finally, I’ll add the CompareTo
Angular Directive which will help us with comparing the value of form fields. I’ll place it in the /js/dependencies
folder.
So let’s see where we are after these dependencies were added. I’ll head back to the browser refresh the page. The console displays an Uncaught reference error that Angular is not defined.
Since this is coming from the toastr
library my hunch is that we have a loading order error. And sure enough if we look at the page source, the toaster
library is being loaded before Angular. I’ll head back to our project and pipeline.js
in sublime and add a reference to Angular here (e.g. 'js/dependencies/angular.1.3.js',
) that will load Angular first before any of the other depedencies.
Let’s head back to the browser and refresh the page. Great, there’s no longer and error.
Now let’s take a look at some form validation in signup.ejs. There are three components to the form field validation we’re going to perform. So taking a look at the name field. We first use the ng-class directive to create the has-error
class if the field name is invalid and dirty. This will insert a red border around the input field. Next we’ll configure the validation parameters of the field itself. In this case we’re requiring that the field have a value as well as have a maxlength of 50 characters. Finally, we’ll set-up the text for our error message here.
1 2 3 4 5 6 |
|
Let’s see this in action. I’ll go back to the browser and type in a name. Now If I remove the name, the required validation is triggered and if I add to many character’s the maxlength validation will be triggered.
Let’s go back to signup.ejs and review the remaining form fields configuration.
So the Title field’s configuration is identical to the name, the title is required and has a max length of 50 characters.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
The Email field is required and requires a properly formatted email address.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
The Password field is required and must be at least 6 characters. Also notice that we’re using the CompareTo directive to compare the password field with the confirmPassword model.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
The Password Confirmation field is required and must match the Password Field.
1 2 3 4 5 6 7 8 9 10 |
|
Finally, we disable the form submission button via the ng-diabled
directive until all of the form fields have valid values.
1 2 3 4 5 6 7 8 |
|
Okay let’s go back and take a look at the validations in action. I’m going to refresh the browser.
Believe it or not that rounds out our signup page. In the next episode we’ll start to flesh out our initial API that connects the signup page with an endpoint that creates a user account via a model into a database.
You can find all of the source code for this episode at the activityOverlord20 repo on github.
And If have a moment plese follow me on twitter and be sure to signup for the Sailscasts mailing list so I can finally prove to my wife that there are actually folks watching this stuff. As always thanks for watching.