Let’s Play MEAN, Part IV

Building a starter repo from scratch.

Joshua Comeau
7 min readDec 9, 2014

Welcome back to this exploratory dive into the MEAN stack.
We’re building a seed repo we can use for all future MEAN projects.
This is Part IV — Does your past include a reading of Part I?

Angular Adventure.

Woohoo, time to start preparing our front-end for awesome app action with Angular.

Warning: Not a beginner Angular tutorial.
Unlike the rest of the MEAN stack, I do have some experience with Angular. I’m not going to go too far in depth here, but if you’re willing to do a bit of googling, hopefully you won’t get too lost!

If you’re curious how I learned, I did the official Angular tutorial, and got a pro account with Egghead. That and a lot of experimentation and StackOverflow.

Design Choices
I’m partial to the newer ControllerAs syntax introduced in Angular 1.2. It feels way more object-oriented, and I like to disconnect from $scope as much as I can. That said, all the tutorials still use the older syntax, there’s way more help on StackOverflow for the older syntax, and some things can be a bit of a pain with ControllerAs, so use your judgment. Check out this amazing blog post covering the newer syntax.

I’m also partial to keeping all of the components in their own module, and just injecting all those modules into the main app one at the end.

Index

You’ll remember all the way back in Part II, we created a very barebones HTML file. Let’s add in some Angular!

<!—- public/index.html -->
<!doctype html>
<html lang=”en”>
<head>
<meta charset=”UTF-8">
<base href=”/”>
<title>MEAN stack template</title>

<!—- JS -->
<script src=”assets/libs/angular/angular.js” type=”text/javascript”></script>
<script src=”assets/libs/angular-route/angular-route.js” type=”text/javascript”></script>
</head>
<body ng-app=”mainApp”>
<div class=”container”>
<!-- HEADER -->
<header>
<a href=”/”>Mean Stack Template</a>
</header>
<nav>
<!— LINK TO OUR PAGES. ANGULAR HANDLES THE ROUTING HERE →
<ul>
<li><a href=”/things”>Things</a></li>
</ul>
</nav>
<!—- ANGULAR DYNAMIC CONTENT -->
<div ng-view></div>
</div>
</body>
</html>

Super basic so far. We have a header that links to our index, and a nav with a sole link to our Things index. Clicking a link will populate the ng-view with the contents of that partial. Lets make those partials, super quick:

<!-- public/components/things/things.index.html --><div>
<h1>Things Index!</h1>
<p>{{thing.tagline}}</p>
</div>
<!-- public/components/dashboard/dashboard.index.html --><div>
<h1>Dashboard Index!</h1>
<p>{{dash.tagline}}</p>
</div>

Alright, we have our layout file and our two partials. Let’s link it all together with routes

Routes

// public/app.routes.jsangular
.module(‘appRoutes’, [])
.config([‘$routeProvider’, ‘$locationProvider’, function($routeProvider, $locationProvider) {

$routeProvider
.when(‘/’, {
templateUrl: ‘/components/dashboard/dashboard.index.html’,
controller: ‘DashboardController’,
controllerAs: 'dashboard'
})
.when(‘/things’, {
templateUrl: ‘/components/things/things.index.html’,
controller: ‘ThingController’,
controllerAs: 'thing'
});

$locationProvider.html5Mode(true);
}]);

The first thing we do is create a new module named appRoutes. The syntax for setting a module is to call .module() with two arguments: a string for its name, and an array for its dependencies. Since we don’t inject any other modules at this stage, we move on to the config.

Module configs are where you inject providers — things like factories and services. We need two of Angular’s built-in ones, $locationProvider and $routeProvider.

$routeProvider provides the main logic here, with its .when method. It takes two arguments — the path, and a route object with the data for routing that path. We’ll pass in a root relative path to the template, the controller we’re using, and the controllerAs name.

$locationProvider helps us sort out our linking style. Perhaps you’ve seen URLs that look like this?

http://www.somesite.com/#/index.html

Compare that to clicking anchor text that leads somewhere else on the page:

http://www.wikipedia.org/dragons/index.html#species

HTML has a standard where if you click on a link where the target starts with a hash, it doesn’t fire off a real HTTP request, it just moves your position on the page to wherever the <a title=“species”> tag exists.

Single Page Apps do something kinda similar; when you click on a link, it doesn’t really fire off an HTTP request, it fires off an AJAX request and swaps out certain content on the site.

HTML5, however, has a more graceful way of handling this, and you can have real-looking links without the hash. It degrades gracefully to use the hash system on older browsers.

Having trouble? The other piece to this working is having a <base> tag on your index.html, that tells Angular what the base URL is. We’ve added one on our index, but in case you missed it, add this to your <head>:

<base href=”/”>

Things and Dashboard Controllers

We’ve now made reference to controllers that don’t exist! I’ll spare you the trial-and-error drama we went through in Part II, and just add the stuff we need to.

// public/components/things/things.ctrl.js

function ThingController($scope) {
this.tagline = “There will be many things here soon.”;
}
ThingController.prototype.someMethod = function() {};ThingController.$inject = [‘$scope’];
angular.module(‘mainApp.things’).controller(‘ThingController’, [‘$scope’, ThingController]);

So, there’s a loooot of syntax here. Part of that is ControllerAs doing its thing, and part of it is wanting to avoid minification woes by specifying our dependents as strings.

We define our controller as a function up top, and that’s where all our variables and such go. We can also define methods on the prototype, as exampled by someMethod.

You may have seen, in other tutorials, having the controller be an inline, anonymous function in the .controller() method. We can do it that way, but it just feels so much nicer to have a standalone function at the top.

Theres a caveat, though, and it’s a bit confusing. Hopefully this example will illustrate.

You’ll note that at the top, our function has $scope as an argument:

function ThingController($scope) {

And when we create our controller, we don’t pass in that argument:

.controller(‘ThingController’, [‘$scope’, ThingController]);

How can this be? We pass the reference to a function without any arguments, and yet somehow that function is supposed to have access to them?

Angular, in its infinite wisdom, provided a way to deal with this, and it’s through $inject:

ThingController.$inject = [‘$scope’];

as long as we have this line in there, the function will have its arguments ‘injected’ at a different point, and everything will work as intended. This was a major pain point for me a few weeks ago, so hopefully I can save someone else the trouble!

For the dashboard controller, we’ll do the same thing.

// public/components/dashboard/dashboard.ctrl.js

function DashboardController($scope) {
this.tagline = “Welcome to the dashboard!”;
}
DashboardController.prototype.someMethod = function() {};DashboardController.$inject = [‘$scope’];
angular.module(‘mainApp.dashboard’).controller(‘DashboardController’, [‘$scope’, DashboardController]);

Modules

We’re making reference, in our controllers, to mainApp.dashboard and mainApp.things, but we haven’t instantiated them yet! Let’s do that.

// public/components/things/things.module.js
angular.module("mainApp.things", []);



// public/components/dashboard/dashboard.module.js
angular.module("mainApp.dashboard", []);

App

Alright, we’ve created the 3 custom modules I want to start with. appRoutes, mainApp.things, and mainApp.dashboard. Let’s inject them all into our main app module. We’re almost good to go!

// public/app.js

angular.module(‘mainApp’, [‘ngRoute’, ‘appRoutes’, ‘mainApp.things’, ‘mainApp.dashboard’]);

Tying it all together

The only thing left to do is include all of our Javascript files in our index. Put this in the <head> of your index.html:

<!—- JS -->
<script src=”libs/angular/angular.min.js”></script>
<script src=”libs/angular-route/angular-route.min.js”></script>
<!—- ANGULAR CUSTOM -->
<script src='components/dashboard/dashboard.module.js'></script>
<script src='components/dashboard/dashboard.ctrl.js'></script>

<script src='components/things/things.module.js'></script>
<script src='components/things/things.ctrl.js'></script>


<script src='app.routes.js'></script>
<script src='app.js'></script>

The order matters.
We need to make sure we aren’t referencing things that don’t yet exist!

When our browser’s JS engine gets to dashboard.ctrl.js, it needs to know about the mainApp.things module, so we need to instantiate the module first. When we get down to app.js, we inject all the other modules we created (appRoutes, mainApp.things, mainApp.dashboard), so app.js needs to come last.

Let’s fire it up!

Awesome. We can see that appRoutes is picking up on our current path, fetching the right template and controller, and populating ng-view with it. Our dashboardController is then populating {{dash.tagline}} with our fabulous welcoming message.

But that’s not all. Click on ‘things’, and holy shit, the view changes:

The request gets sent straight to appRoutes, which detects the desired template and controller, and makes the switch.

What’s missing?

We actually sort-of have an app now! Node, Express, Mongo and Angular are all being used, and working as advertised.

No matter what kind of app we build, though, we’ll almost certainly need to grab data from our server or another back-end API.

Initially I was going to build that service right here, but I decided that I’d rather do it test-driven style. So, keep reading to see how I did it!

Continue reading Part V: Front-End Testing

--

--