sailsCasts

Learning about sails.js one screencast at a time.

Building an Angular Application in Sails: Ep6 - User Authentication in Angular and Sails.

| Comments

Signup for the Treeline Beta here.

Follow me on twitter here.

Subscribe to the sailscasts mailing list here.

Transcript

Howdy.

In the last episode we finished out the signup page. We now want to take that information and allow users to login, better known as user authentication. So far we’ve created one Angular module for the signup page connected to our Sails API. And this is under a broader category of public pages or those pages that don’t require a user to be logged in to see them. We also need a homepage that will also be public. Finally we need a view, I’m going to call the dashboard that will require that the user be logged in to our back-end.

So we have three Angular modules for activityOverlordv2.0. You can of course accomplish the same end result in a variety of different ways, but I’ve chosen this approach for simplicity and ease of SEO.

Ok, let’s start with the homepage.

We’ll first replace the current mark-up in homepage.ejs within the view folder with this new mark-up. The markup contains our top level navigation, which includes the sign in button that triggers a PUT request to /login, as well as a sign-up button that triggers a GET request to /signup. I realize I’m going through this pretty fast but don’t despair, I have a repo that has all of the code for your review.

Let’s look at the Angular module and controller for the homepage. The homepage and signup module’s are identical both inject the toastr and compareTo services. The homepage controller adds a new method to the scope called submitLoginForm. Similar to signup form, submitLoginForm initially sets the loading state to true. We then make a PUT request to /login passing in an email field as well as a password field from the $scope object. If the request is successful, we’ll make the equivalent of the a GET request to /. If we’re unsuccessful we’ll handle the known errors, in this case a bad email/password combination through the toastr service and return. We’ll also handle any unexpected error here. Finally, in either case we’re toggle the loginForm loading state back to false.

So now that we have the front-end set up for the Homepage, let’s switch gears to the back-end Sails API.

The first thing I want to do is handle the PUT request to /login. So I’ll add that route to the routes.js file here. We want this to trigger a login action in our UserControlller. Next, we’ll head over to our user controller in UserController.js. So let’s add the add the login action. The next block of code I’ll add first takes the email address that was passed from the login form and searches our user model for an existing user. If it doesn’t find one we’ll send back a 404 status which will trigger the toastr message in the HomePageController. Next, we’ll be using another machinepack, called machinepack-passwords. Within the machinepack we’ll use the checkPassword machine passing in the user provided password from the login form as well as the encrypted password found by the findOne method of our user model. Within this machine we handle any unexpected error first and then if the passwords don’t match we again send a 404 status via res.notFound(). For reference, these responses can be found in the responses folder here. If we get a match, we need to log the user in so we’ll create a new parameter on the session via req.session.me and give it the value of the id of the user we found earlier with findOne method of the user model. The last thing we need to do is let the front-end know everything went well and we do that by returning with res.ok().

Let’s see all of this in action. I’ll start Sails with sails lift and we’ve got an issue here. I know what it is because I’m old and forgetful. So, I’m going to go into the routes.js file and you’ll see that I just missed a comma here. So let’s go back and try that again. Okay, Sails is up and running, let’s go to the browser and let’s hit localhost:1337. Okay, so the reason we’re getting a 404 not found is we don’t have a route that handles a GET request to /. Now we could remedy this very quickly by going back to the routes.js file, and we’ll create a route here, that handles a GET request to /, which will trigger the homepage and we’ll restart Sails here…go back into the browser and refresh and ther’s our homepage and that’s great. But what I really want is a way to marshall between whether a user is authenticated, that is, logged in, and if they’re logged in I want to display one front end and if they’re not logged in a different front end. I can do that by adding a page controller to Sails. So let’s do that. First, I’ll add the controller via sails generate controller page. Now let’s go back into Atom and we have our PageController here which is empty. I’m going to add an action here showHomePage.

We first check if the user is logged in via the me parameter on the session, so, if req.session.me does not exist, I want to load the homepage and I’m going to do that by return res.view('homepage'). Otherwise if the user is logged in, I’m going to use the findOne method of the user model to look for that user. So I’ll pass in the userId which is the value of the me parameter on the session. First I’ll look for an unexpected error here and I’ll handle that with res.negotiate if it exists. If I don’t find a user, I’m going to return the user to the homepage. Now if I do find a user, I’m going to return a view that we haven’t created yet called dashboard. I’m going to bootstrap some information that I found via the findOne method of the user model on the page itself.

Before we create the dashboard view you may be asking what’s going to trigger this showHomePage action. Well, we’re going to do that by going into, routes.js and substituting this current view with our PageController and our PageController action showHomePage. So now any time there’s a GET request to / our page controller will marshall which front end the user receives.

Now let’s implement our dashboard page. The mark-up for the dashboard page is straightforward. As with the signup and homepage views, the dashboard view will have some top level navigation. The part I want to concentrate on is the bootstrapped data we get from the page controller’s showHomePage action. So using ejs we’ll add the parameters of the me object from the showHOmePage action on to window.SAILS_LOCALS.

Next, let’s add the very minimal dashboard module and controller. I’ve added these files under a separate folder named private to distinguish between the Angular code that will be executed when a user is authenticated and code that is public and does not require authentication.

I want to ensure that the new Angular modules I’ve added will be loaded before any controllers, so I’ll go back to pipeline.js found in the tasks folder and add the path to the two new modules — HomepageModule and `DashboardModule’. This will insure that the modules will be loaded into the page first.

Okay, let’s see this all in action. So I’ll start Sails, with sails lift, and then navigate my browser to localhost:1337. In the previous episode we created an account, nikola@tesla.com with the password 123456 and I’m going to use that now. Now if you don’t have that user created, no big deal, just click on the sign up now button and create that user, nikola@tesla.com with the password 123456, then go back to localhost:1337 and start from there…and we have our dashboard page. And if we view the page source, we can see that we have our bootstrap user right here on the page. So let’s go back to the PageController really quickly here…req.session.me existed, we used that with the findOne method of the user model…it found the user and then passed the user object via the me parameter and bootstrapped it onto the dashboard page.

So there are a couple of things we still need to implement here. We have this sign-out link, that if i click on it it’s going to give us a 404 and that’s because we don’t have a route that handles that link…so this link is making a GET request to /logout and we don’t have a route for that so let’s take care of that first. So now that GET request to /logout will be tied to or will trigger the UserController and this new action on the controller. So let’s go up to UserController and let’s add a logout action.

So similar to our login action we’re going to look for the user id on req.session.me…we’re going to pass that to the findOne method of our user model…we’re going to handle the errors if they exist and if it finds a user, we’re going to essentially log that user out by making req.session.me equal to null. Then we’re going to return with a new response, backToHomePage(). So let’s take a look at that response.

So this response is basically for convenience…if logout were an AJAX request, Sails will know that that request just wants a JSON status code response. In our case we’re making a GET request via the browser, so we just want to redirect back to / and let our page controller handle it. Okay let’s see this in action.

So I’ll go and restart Sails, and we’ll go back and log-in. So now when I hit the signout link the PageController sends me back to the homepage.

There’s just a couple of other things we need to do. Within the UserController, signup action we want the user to be logged in after they’re signed up. So we’ll do that right here. And within the signup page controller, on a successful signup we made a GET request via the browser to /user and now we just want to ake that /.

Let’s go ahead and restart one more time. And we’ll now go to signup, and I’ll create a new user b, b@b.com…and everything works. So the user was created and logged-in…and let’s take a look at the page source…and there we are there’s our bootstrapped data.

Thanks for making it through the many voices of Sailscasts. Although it might seem that there are different Irl’s on this Sailscast, I assure you that it was the same ole guy throughout…al beit Allergy-challenged. So now we know how to create a user, save it to a database using the Sails ORM, and then use that information to authenticate a user marshalling between different Angular front-ends depending upon the user’s log-in status.

There’s been quite a bit going on in Sails and Treeline so expect an update shortly via the Sailscasts mailing list. If you’re not signed up, head over here to subscribe and you can always follow me on Twitter here.

Building an Angular Application in Treeline Sneak Peak: Implementing the Signup Page in Treeline.

| Comments

Signup for the Treeline Beta here.

Follow me on twitter here.

Subscribe to the sailscasts mailing list here.

Transcript

Howdy.

So, I wanted to do a quick screencast previewing a new project from the Sails crew called Treeline. So, let’s get busy.

In the last two episodes we implemented the /signup request via Sails blueprints and then a custom controller action. Today, I want to show you how I would do this in Treeline. So I have a Treeline project here, which should look very similar to a Sails project because that’s what it is with some very powerful additions. I’ve copied the assets folder from our existing activityOverlord20 project to the assets folder here. So we have the same front-end asset structure, but I’m going to create the back-end api in treeline.

So here I am in the Main Treeline Dashboard. We’ll go to apps, we’ll create a new app, and not surprisingly we’ll call it activityoverlord20. And the first thing we’re going to do is create our model. Now, in the past episode we created a user mode. We’ll add attributes to that model the first being name and notice that I’m going to add an example, and treeline will infer which type is appropriate. Next, I’ll add the other attributes title, email, password, and gravatar url.

So now that we have our user model let’s define the two routes that our front end requires. The first route will be GET /signup. This takes us into the circuit view. Since we just want to display our signup.ejs view we can specify that in an empty circuit here. Okay, next I’ll create the route to POST /signup. As you may recall we first wanted to take the password from our form and encrypt it. We used node-machines to do that. You can think of treeline as the visual interface for node-machines. So I’ll just drag the encrypt password machine as a part that will fit on something called a circuit. And then I’ll access the password field from our signup page via a route paramter.

Next we want to convert an email address into a gravatar url. I’ll search for the gravatar machinepack and install it. And from with that machinepack I’ll get the getImageUrl machine and put it on our circuit as well. Now similar to what we did with the encrypt password, I need to get our email address from our form and I’ll do that with a route parameter.

At this point we wanted to make sure that we had unique email addresses and if we didn’t to let the front in know via a 409 status code. So I’ll drag a findone user machine over to our circuit and look up the email address provided by our form in the user model. If a record is found, we know the email address is taken and we want to let the front end know so I’ll respond with a 409 status. If a record is not found, we want to go ahead and create the user so I’ll drag a create user machine over and start connecting the user model with our form fields. We’ll do this mainly through route parameters, so for the name we’ll get the route parameter, for the password we’re going to actually get the output of our encrypt password machine. For the email we’ll go ahead and use a route parameter, we’ll do the same for the title field, and finally for the gravatar url we’ll use the output of the gravatar machine.

Okay let’s see all of this in action. So we’ve built the api in Treeline and Treeline syncs the controller and model with our local project when I lift it using Treeline preview. So let’s head to our browser and navigate to localhost:1337/signup which will make the GET request to /signup and our signup view. Next, let’s go ahead and create a User record – Nikola Tesla.

Let’s go back to the signup page and create Nikola Tesla again and see if our duplicate email message is triggered.

And there it is…so this really just scratches the surface of the power of treeline. In addition to the activityoverlord20 screencasts I plan to do additional videos on this new tool. If you’re interested in using treeline, head over to treeline.io and sign up for the beta. As always thanks for watching.

Building an Angular Application in Sails: Ep5 - Creating Custom Actions and an Intro to Node-machines.

| Comments

Follow me on twitter here.

Subscribe to the sailscasts mailing list here.

The repo for this episode can be found here.

Transcript

In the last episode we implemented a request to /signup using a Sails blueprint action. The default blueprint create action is fine but I really don’t want to save passwords in clear text and I also want to generate a gravatar for each user. Let’s overwrite the blueprint create action with our own signup action. This will also let me introduce a new concept to the Sails community — node machines.

So let’s head back to the editor and open up the UserController.js file. I’m going to create an empty signup action here:

1
2
3
4
5
signup: function(req, res){

  return res.ok();

}

Now, let’s navigate your browser to node-machine.org which is kind of the mothership of node-machine information. The first thing to understand is that machines are built on the existing and very powerful node module framework. Second, the node-machine specification and the machines located at nodemachine.org are completely open source and MIT licensed. Third, machines are packaged up in something called a machinepack that can be required just like any other node module from npm. So, I’ll show you how to create node-machines in future screencasts, for now, we’ll be consumers of a couple of them.

I know I want to encrypt passwords so let’s browse the existing machinepacks. So here’s one called passwords. And there’s a machine called encryptPassword that looks kind of interesting. With machines you know exactly what inputs you can use and exactly what you’ll get out from the exits. I’m just going to copy the example usage here and paste it into my signup action. I’ll also move my existing response to the success exit of the machine.

I’m also going to move our response into the success exit. Finally, I’ll grab the password as an input to the machine from a request parameter and if there’s an error I’ll let Sails handle it with res,negotiate and respond with the error. I’m also going to change the default result arguent to a more meaningful name like encrypted password.

So in addition to encrypting the password I want to create a gravatar url from the email address that we get from our user. Let’s go back to node-machine.org and browse to the gravatar machinepack. The getImageUrl machine returns a URL of a gravatar based upon an inputted email address. This machine actually has two ways we can set it up. The example usage is using `execSync(), which allows me to use the machine sychronously. I want to use the machine asynchronously so I'll use.exec()“. Therefore, I’ll also provide both success and error handlers.

So we’ve encrypted our passwords and generated a gravatar URL. Next we’ll save these attributes along with the other fields in our signup form using the create method of our user model. I’ve also added an attribute lastLoggedIn and assigned it the value of the date method. If we get an error I’ll let Sails handle it with res,negotiate and respond with the error. Otherwise I’ll return a response with the id of the new user record as json.

We need to install the two machines we’re using. Open up the terminal window and type npm install machinepack-passwords -save. Next, we’ll install the gravatar machinepack by typing npm install machinepack-gravatar -save.

Okay, let’s make sure all of this works. Restart Sails using sails lift. Again we’re going to get this message about migration. Our choices are: Safe, Drop, and Alter

If I set migrations to safe, sails doesn’t do anything other than create a connection to the database and run queries. It’s the default environment for production and should be used whenever you are working with data you don’t want to risk losing.

Instead of trying to migrate the data in the database this mode drops the database and creates brand new tables, essentially giving you a fresh start. If you have a bootstrap file that resets your data on every lift then the drop migration is a good way to get going when your models are constantly changing in the beginning of a project.

alter is next step up from drop. The difference is that before it drops the database it tries to store all the records in memory and when the table has been re-created it attempts to re-insert them into the new data structure. It’s useful if you have a very small data set and are making trivial changes to the data schema. We’ll ultimately be using safe mode but for now we’ll use alter. We can set this mode by going into the config/models.js file and uncommenting this line.

So let’s lift sails using sails lift. Navigate your browser to localhost:1337/signup. I’ll add another one of my heros Neil Armstrong. And as you can see we have a new record with an encrypted password, a gravatar URL and our lastLoggedIn Attribute.

So far we’ve been using our user model without any attributes. And that’s fine, especially for nosql databases. In this current project, we’re using Sails local disk database that has characteristics of a no-sql database. We’ll be swapping it out for mongo in an upcoming episode. At this point, I want to prevent duplicate users and I also want to prevent the storage of fields other than those I define in my model. This is just a double check because we’re specifying the fields we want to create in our controller action. So we’re currently in the user model via the /models/User.js file. I’m going to add some attributes here. We’re already doing a bunch of validation client side, but since those are not necessarily secure, we’ll reinforce the validation on the back-end. I’ll use the required attribute on some of the fields and the unique attribute on the email field. This will prevent duplicate email addresses from being saved.

So let’s go back to our signup action in the user controller. First I want to parse the error I get back from Sails regarding the duplicate email address. Let’s log the error and take a look at what we get back. So in POSTMAN with Sails lifted. I’ll create the Nikola Tesla record. Now when I try to add Tesla again, I get the unique error. So let’s go back into the action and handle that error. This also let’s me introduce you to custom responses. So if an error exists and it has to do with the email attribute, and the rule that’s being violated is the unique attribute, then I want to respond with this emailAddressInUse function. I’m going to place that function as a custom response in the responses folder. Let’s go ahead and take a look that resonse. So, I’ll get access to the response and then return a more easily parsable object back to the client. So let’s re-lift Sails and in POSTMAN try to add Mr. Tesla again and we get our error object.

Let’s provide for a better user experience to this error on the front end. First we’ll go into the SignUp Module and inject the toastr service we started in the last episode. This will provide us with some great looking messages. Next, we’ll do something similar in the SignupController injecting the toastr service into our controller. So now we need handle the duplicate error response from Sails passing an error message to the user. In the last episode we already set this up in the markup of our signup page. So start Sails and navigate your browser to localhost:1337/signup. I’ll attempt to create Nikola Tesla one more time. And there’s our duplicate user error message, however, the loading state hasn’t changed as evidenced by the animated Create Account button. Okay the reason for this is something most refer to as old age. But the good news it’s an easy fix. So, head back to SignupController and change signupForm.location: false to signupForm.loading: false which is what actually was intended but for you know what. Just to show you it’s fixed I’ll add Nikola again for the last time. And we get both the message and the button with the correct loading state.

Alright, in the next episode we’ll add our login state through a request to /login, our profile page, and a database switch to mongodb. Thanks as always for watching and be sure to follow me on twitter and signup for the Sailscasts mailing list. Also go checkout and signup for the beta of the Sails team’s latest creation Treeline.io.

Building an Angular Application in Sails: Ep4 - Implementing Requests in Angular to a Sails API.

| Comments

Follow me on twitter here.

Subscribe to the sailscasts mailing list here.

The repo for this episode can be found here.

Transcript

Howdy from Austin and welcome back.

It’s now time to get into implementing our Sails back-end api for the signup page. In terms of workflow, we’re going to let the signup page drive the requirements of our Sails back-end. So looking at the signup page I want to find all of the places where the frontend needs to make requests of our backend.

Not to get too nerdy here, but really we’ve already made a request from our browser to our Sails back-end when we loaded the signup page. When I navigated to localhost:1337/signup with the browser, the Sails web server passed our request to the router, which parsed the request, in this case, /signup and matched it with a handler in the routes.js file ultimately responding with this view. So now that we have the signup page rendered in our browser, what other elements in the page need to make requests?

There are at least three different elements that will make requests to the Sails back end api. They include / when the user clicks the activityOverlord2.0 logo, to /login when the user clicks the Sign In button, and to /signup when the user clicks the Create Account button. I’m going to implement the request to /signup first. Now, some of you might be wondering, wait, we’ve already handled a request to /signup. And that’s true but it’s to GET /signup, where GET is the HTTP verb that the browser tpically uses to request web pages. This new request, however, will use the HTTP verb POST as in POST /signup. If this is starting to make your head spin, get your balance and head over to this episode which explains HTTP routing in nauseating detail. Actually I, I hope it isn’t nauseating.

Ok, this POST /signup request will take the gathered contents of this form and send it to the Sails server via an AJAX request from Angular.

So let’s set this up and go b ack in our text editor, wait where’s sublime. I love sublime text but I wanted to experiment with the Atom editor from github for the next serveral episodes….anyway we’re in signup.ejs. And we need to implement this function that the ng-submit directive is pointing to: `submitSignupForm()“. But where do we declare that function? We’ll declare it in our controller which we pointed to via the ng-controller directive here. The actual controller code is contained in a file named SignupController.js by convention because really we could’ve named the fie foo. So here’s where we actually created the controller. So I’ll declare the function here. But this function has no connection or binding to the markup. We’ll use the Angular $scope object to make this connection. Angular characterizes the $scope object as the glue between the controller and directives in the markup. So on the onehand we have controllers contained in javascript files and on the other we have directives contained in mark-up. And the $scope object is used as the bridge between them.

So let’s fire up sails using Sails lift and go into the chrome browser and navigate to localhost:1337/signup. And as you can see here we’ve got an error…our $scope object has not defined. What’s happened is the $scope has not been injected into our function. But we can easily remedy that by passing the scope as an argument into the function. Now when we refresh the browser, we no longer have an issue. But we’re not out of the woods yet.

Angular determines a controller’s dependencies from the arguments to the controller’s constructor function. So if we minify our JavaScript for the Signup controller, all of the function arguments including $scope would be minified as well. So $scope could turn into the letter a and Angular’s dependency injector would not be able to identify services correctly. But there’s an easy fix here. I can add an array to the constructor here that adds a string for $scope and since strings aren’t minified, Angular has a way to match the name with the argument. Let’s go back into browser and refresh and make sure everything works.

Okay great, Next, we’ll enable the loading states we created in the previous episode. I’ll set-up the initial loading state by adding another attribute to the scope object:

1
2
3
4
// set-up loading state
  $scope.signupForm = {
      loading = false;
  }

Now, don’t be confused by my adding the signupForm attribute between the scope object and the loading attribute. I’m just gonna namespace everything concerning the signup form to under this attribute. So when our submitSignupForm() function is executed we want the loading state to change to true. If we look at the markup and specifically the create account button, you can see what’s changing based upon the loading state. If the loading state is false, the button will display the Create Account text. If it’s true, the button will display our spinner animation with the text Preparing your new account within the button. Okay, let’s see if this works. I’ll go back to the signup page and since we have validation I need to put in some valid values and when I click the create account button you can see the loading state change.

So now let’s add the ajax request to our function and we’re going to do that with an Angular service $http. If you’ve ever done an ajax request in jquery, this is going to look very familiar. First, let’s inject it into the function and the array like we did with the $scope object. So we’re going to put in the path and any data we want to send with the request. In order to bind the form fields to these data attributes we’re going to use $scope again. And remember the $scope is linking the values that we’re capturing in the mark-up via the ng-model directive. So let’s do that for the rest of our fields in the form. We’ve got title, and email, and password. Okay great, now let’s declare our success and error handlers. So on success I want to change the location of the browser to /user. This has the effect of making a GET request to /user. If the request generates an error, I want to catch that error and for now just log the response. Finally, I’ll create this catchall function and either way I want to reset the loading state back to false.

We’re now going to transition from the front-end to the back-end. From the angular framework to the Sails framework. Our goal is to create a user by gathering up account information and sending it to Sails. So let’s implement some stuff and then I’ll circle back and explain what we did in more detail.

Head over to the terminal and type sails generate api user. With this simple set of commands and a bunch of tools and automation from Sails, we have almost everything we need for our API. Start the Sails server by typing sails lift.

Because we created a model, Sails is asking us a question about migrations, we’ll come back to this. For now I’m going to use the second option — alter.

Next, let’s go into the browser and navigate to localhost:1337/user. We’ve just used one of Sails blueprint shortcut actions. By navigating to ‘/user’ we made a GET request to the server for all the user records. And since we haven’t created any records yet, not suprisingly we get an empty array. But using the create action blueprint shortcut I’ll create our first user record — Humphrey Bogart. Sails also has blueprint rest or RESTful actions. From within POSTMAN, really one of the best tool on the planet for testing routes, I’ll make a POST request to /user with these route parameters for our next user Aimee Mann. Chances are you’ll create your own Controller Actions by the time you get to production. However, Blueprints are incredibly useful in the design phase of your API. If you interested in learning more about Blueprint capabilities, head over to this episode where I break them down in a bunch of detail.

So the path we want to associate with create a user is /signup. So head back into the editor and open up \config\routes.js. I’m going to add the route POST /signup which will be handled by the UserController.create action. Let’s restart Sails and navigate to the signup page. Finally, I’ll signup my hero Nikola Tesla. And there’s our list of records.

So briefly I want to go over what we’ve done. We created a route that connects the request to a controller and action. When the api was created, Sails provided us with a user controller and a create action (via blueprints) automatically. Sails also created a user model that has model methods like .create, .find, .update, and .destroy. Finally, the user model can retrieve and save records from a variety of databases via adapters.

In the next episode we’ll transition from Sails blueprints to our custom action. I’ll also introduce a brand new Sails concept — node machines. Thanks as always for watching and be sure to follow me on twitter and signup for the Sailscasts mailing list. Also go checkout and signup for the beta of the Sails team’s latest creation Treeline.io.

Building an Angular Application in Sails: Ep3 - Understanding Asset Delivery Options in Our SignUp Page.

| Comments

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
<!DOCTYPE html>
<html>
  <head>
    <title><%- title %></title>
  </head>
  ...

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
'GET /signup': {view: 'signup'}

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
<!DOCTYPE html>
<html>
  <head>
    <title>Sign Up for Activity Overlord (angular)</title>

    <!-- Viewport mobile tag for sensible mobile support -->
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  </head>

  <body ng-app="SignupModule" ng-controller="SignupController" ng-cloak>
    <h1>The Signup Page
    <!--SCRIPTS-->
    <!--SCRIPTS END-->
  </body>
</html>

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.jsandSignupController.js. Okay, first let's take a look atSignupModule.js` and I’ll add the bare bones Angular code to define our new module.

1
angular.module('SignupModule', []);

Next, I’ll open SignupController.js and add the bare bones Angular code to define our new controller.

1
angular.module('SignupModule').controller('SignupController', function(){});

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
...

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

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

...

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
...

    <!--SCRIPTS-->
    <!--SCRIPTS END-->
  </body>
</html>

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
<!-- Also, if signup.name.$dirty is true, show the message depending upon the particular properties truthiness (e.g. required
and/or maxlength) -->
<span class="help-block has-error" ng-if="signup.name.$dirty">
  <span ng-show="signup.name.$error.required">Name is required.</span>
  <span ng-show="signup.name.$error.maxlength">The name cannot be more than 50 characters.</span>
</span>

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
<!-- T I T L E -->

<div class="control-group form-group col-md-12"
  ng-class="{'has-error':signup.title.$invalid &&
                        signup.title.$dirty}">
  <label>Your title</label>
  <input type="text" class="form-control" placeholder="e.g. Genius" name="title" ng-model="signupForm.title" ng-maxlength="50" required>
  <span class="help-block has-error" ng-if="signup.title.$dirty">
    <span ng-show="signup.title.$error.required">Title is required.</span>
    <span ng-show="signup.title.$error.maxlength">The name cannot be more than 50 characters.</span>
  </span>
</div>

The Email field is required and requires a properly formatted email address.

1
2
3
4
5
6
7
8
9
10
11
12
<!-- E M A I L -->

<div class="control-group form-group col-md-12"
ng-class="{'has-error':signup.email.$invalid &&
                      signup.email.$dirty}">
  <label>Your email address</label>
  <input type="email" class="form-control" placeholder="nikola@tesla.com" name="email" ng-model="signupForm.email" required >
  <span class="help-block has-error" ng-if="signup.email.$dirty">
    <span ng-show="signup.email.$error.required">Email address is required.</span>
    <span ng-show="signup.email.$error.email">Not a valid email address.</span>
  </span>
</div>

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
<!-- P A S S W O R D -->

<div class="control-group form-group col-md-6"
ng-class="{'has-error':signup.password.$invalid &&
                      signup.password.$dirty}">
  <label>Choose a password</label>
  <!-- Added the compareTo directive that compares the passowrds -->
  <input type="password" class="form-control" placeholder="at least 6 characters" name="password" ng-model="signupForm.password" id="password" required ng-minlength="6" compare-to="signupForm.confirmPassword" >
  <span class="help-block has-error" ng-if="signup.password.$dirty">
    <span ng-show="signup.password.$error.required">Password is required.</span>
    <span ng-show="signup.password.$error.minlength">Password must be at least 6 characters.</span>
  </span>
</div>

The Password Confirmation field is required and must match the Password Field.

1
2
3
4
5
6
7
8
9
10
<!-- C O N F I R M  P A S S W O R D -->

<div class="control-group form-group col-md-6">
  <label>Re-enter your password</label>
  <input type="password" class="form-control" placeholder="one more time" name="confirmation" ng-model="signupForm.confirmPassword" required>
  <span class="help-block has-error" ng-if="signup.confirmation.$dirty">
    <span ng-show="signup.password.$error.compareTo">Password must match.</span>
    <span ng-show="signup.confirmation.$error.required">Confirmation password is required.</span>
  </span>
</div>

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
<!-- C O N F I R M  P A S S W O R D -->

<!-- Disable signup button until the form has no errors -->
<button class="btn btn-primary btn-lg btn-block" type="submit" ng-disabled="signup.$invalid">
  <span ng-show="!signupForm.loading">Create Account</span>
  <span class="overlord-loading-spinner fa fa-spinner" ng-show="signupForm.loading" ></span>
  <span ng-show="signupForm.loading">Preparing your new account...</span>
</button>

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.