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