Lets Get Fancy

Part 2 of learning expressjs/node

Jason Manners
Hello There, Expressjs

--

Lets continue our journey, from Part 1 here, and transform our application into something we can expand and continue to build. From here our application will go through a few iterations. First we will restructure our code and then introduce views using jade.

Lets Destroy and Rebuild

At the moment our application is in a single file, app.js. This is great for small things like helloworld, but as soon as you want to add anything significant it quickly becomes unmanageable. We are going to leave our application setup code in app.js and move all other functionality into a lib directory. This will allow the app to continue to grow in a way we can manage.

The structure we want will look something like this

Create the folders /lib and /lib/odyssey.

Since app.js is fairly small there will not be much to break out. Remove the following code from app.js

app.get('/open-doors', function(req, res){
res.send("I'm sorry, Dave. I'm afraid I can't do that.");
});

And create the files /lib/odyssey/routes.js and /lib/odyssey/odyssey.controller.js. These two files will contain our functionality. The routes.js file will map the urls, /open-doors, to functions in odyssey.controller.js, OdysseyController.openDoors. The odyssey.controller.js will contain the function that handles the request.

/lib/odyssey/odyssey.controller.js

This file will contain the function that interacts with the user request. We will use the function we used in app.js and just give it a name.

module.exports = {
openDoors : function(req, res){
res.send("I'm sorry, Dave. I'm afraid I can't do that.");
}
};

This allows us to require this file in another file and use the function openDoor. If you an unfamiliar with this syntax, we are using object literals, more on that can be found here

/lib/odyssey/routes.js

This section can be written in two parts, we require the dependencies, and then we write the functionality.

In this case routes.js will only have a single dependency, odyssey.controller.js, which will look something like this:

var OdysseyController = require('./odyssey.controller');

This will allow us to interact with our controller and use the function we defined within routes.js

Since we are in a new file which does not have access to app that we defined in app.js we need to create a function that we can call with app. To define the route it will look something like this.

function setupRoutes(app){
app.get('/open-doors', OdysseyController.openDoors);
}

Then we have to export it so that when app.js requires this file it can call this function. More on require and module.exports can be found here

module.exports = setupRoutes;

/lib/odyssey/routes.js should look like this

var OdysseyController = require('./odyssey.controller');function setupRoutes(app) {
app.get('/open-doors', OdysseyController.openDoors);
}
module.exports = setupRoutes;

app.js

Now that we have split out our code into multiple files we need to call the function in /lib/odyssey/routes.js and pass in app in order to setup the route that we removed.

var setupOdysseyRoutes = require('./lib/odyssey/routes');
setupOdysseyRoutes(app);

This will pass in our current app which will then have GET /open-doors setup on it.

app.js should now look like this

var express = require('express');
var app = express();
var setupOdysseyRoutes = require('./lib/odyssey/routes');
setupOdysseyRoutes(app);
var server = app.listen(3000, function(){
console.log('Express server listening on port 3000');
});

If we start our application up again, make sure the old one is no longer running, if it is stop it with ctrl+c, and run

$ node app.js

It should behave exactly like it did before. While it looks like we didn’t accomplish much, breaking out that code is huge because now when we want to add another route, which we will do in the next section, we only have to add the function we want to handle the request in /lib/odyssey/odyssey.controller.js and then match it to a url/http-verb in /lib/odyssey/routes.js. Since we are already including routes in app.js anything we add to the funtion setupRoutes will automatically run when we restart the application. Happy Times.

I want my Views

Now that we have restructured the code lets get fancy. We are going to introduce a new route /disconnect-hal which will render some html. Nothing too crazy but incredibly powerful.

But first the setup

Since we didn’t tell our application we were going to be rendering views, and jade, we need to do that now before we introduce our new functionality. We need to do two things in order for this to happen, we need to tell our application where to look for our view files, and we need to tell it that our views are in jade.

We will create /lib/views and tell our application are views are there

var path = require('path');
app.set('views', path.join(__dirname, 'lib', 'views'));

Now to tell our app to use jade

app.set('view engine', 'jade');

app.js should look like this now

var express = require('express');
var app = express();
var path = require('path');
app.set('views', path.join(__dirname, 'lib', 'views'));
app.set('view engine', 'jade');
var setupOdysseyRoutes = require('./lib/odyssey/routes');
setupOdysseyRoutes(app);
var server = app.listen(3000, function(){
console.log('Express server listening on port 3000');
});

Now that that is all taken care of lets go ahead and create the /lib/views, ifyou haven’t already, and /lib/views/layout.jade

/lib/views/layout.jade

This will be our main layout, it will contain the html, head, body, and any other scripts, styles, or tags that will be on all the pages of our site. Page specific content will be defined in separate files.

html
head
title= 'Hello World'
body
block content

/lib/views/disconnect.jade

Lets go ahead and create a simple view which we will call from our new controller function when we create it. This will be a simple view with a title, image, and text.

In order to use the layout we created we need to extend it in our new view.

extend ./layoutblock content
h1= title
img(src='http://bit.ly/VqvT6b', width='100', height='100')
p What do you think you are doing dave

/lib/odyssey/odyssey.controller.js

Now that we have our view we need a function to render that view.

Lets add the property/value to the module.exports variable we have in this controller. Remember since this is a function that will handle a request it will always need at least a req(request) and a res(response).

disconnectHal : function(req, res){
res.render('disconnect', {title: 'Disconnecting Hal'});
}

We have done two important things here. First we are rendering the view we created, and the second thing is we passed an object with the property title. This allows us to use that title property as a variable in our view. Specifically this line h1= title.

odyssey.controller.js should now looks something like this

module.exports = {
openDoors : function(req, res){
res.send("I'm sorry, Dave. I'm afraid I can't do that.");
},
disconnectHal : function(req, res){
res.render('disconnect', {title: 'Disconnecting Hal'});
}
};

/lib/odyssey/routes.js

Now that we have the function to handle the request lets connect it to our route.

 app.get('/disconnect-hal', OdysseyController.disconnectHal);

routes.js should look something like this

var OdysseyController = require('./odyssey.controller');function setupRoutes(app) {
app.get('/open-doors', OdysseyController.openDoors);
app.get('/disconnect-hal', OdysseyController.disconnectHal);
}
module.exports = setupRoutes;

The Results

Now lets go ahead and restart our server, remember to stop the current server if you haven’t and start it again

node app.js

Now if go to localhost:3000/disconnect-hal we should see this

and our project should look like this

What’s Next

Now we will take our well structured simple app and a game of hangman.

--

--