Objectives |
---|
Discuss the use cases for a MEAN Stack |
Connect your client-side Angular application to an Express server |
The great thing about Angular is that it's back-end-agnostic. Since Angular was built with CRUD in mind, as long as your Angular app can query RESTful API endpoints, it doesn't matter the stack of the server. As you've already seen, you don't even need to have your own server to get your Angular app working.
You can follow the instructions here from scratch or use the barebones app in this repo.
- Create a new Node/Express application:
➜ mkdir mean_sample
➜ cd mean_sample
➜ touch server.js
➜ npm init
- Install the following
node_modules
:
➜ npm install --save express body-parser hbs mongoose
- Set up your Express boilerplate:
/*
* server.js
*/
// require express and other modules
var express = require('express'),
app = express(),
bodyParser = require('body-parser'),
mongoose = require('mongoose');
// configure bodyParser (for receiving form data)
app.use(bodyParser.urlencoded({ extended: true }));
// serve static files from public folder
app.use(express.static(__dirname + '/public'));
// set view engine to hbs (handlebars)
app.set('view engine', 'hbs');
// connect to mongodb
mongoose.connect('mongodb://localhost/mean_sample');
// listen on port 3000
app.listen(3000, function() {
console.log('server started');
});
- Set up your folder structure for your assets and views. It should look something like this:
| mean_sample
| node_modules
...
| public
| javascripts
| stylesheets
| images
| templates
| views
- index.hbs
Make sure to create an index.hbs
file inside the views
folder. Your index.hbs
will serve as the "layout" for your Angular app.
- Since
index.hbs
is the "layout" for your Angular app, you want the server to respond with this view every time a route is requested. This will allow Angular to handle routing on the client-side.
You can use app.get('*')
to send every server-requested route to index.hbs
:
/*
* server.js
*/
app.get('*', function (req, res) {
res.render('index');
});
- Require the CDNs for Angular and
ui.router
inindex.hbs
:
<!-- views/index.hbs -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<base href="/">
<!-- angular -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.js"></script>
<!-- ui router -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.18/angular-ui-router.min.js"></script>
<title>MEAN Sample</title>
</head>
<body></body>
</html>
-
Create a new JavaScript file
public/javascripts/app.js
. This is where you'll put all the logic for your Angular app. -
Make sure to require your newly created
app.js
inindex.hbs
:
<!-- views/index.hbs -->
<head>
...
<!-- angular -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.js"></script>
<!-- ui router -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.18/angular-ui-router.min.js"></script>
<!-- custom script (angular app) -->
<script type="text/javascript" src="javascripts/app.js"></script>
</head>
- Add the
ng-app
directive in the<html>
tag inindex.hbs
:
<!-- views/index.hbs -->
<!DOCTYPE html>
<html lang="en" ng-app="sampleApp">
<head>
...
</head>
<body></body>
</html>
- Add the
ui-view
directive inside the<body>
tag inviews/index.hbs
:
<!-- views/index.hbs -->
<body>
<nav class="navbar navbar-default">
...
</nav>
<div ui-view></div>
</body>
Note: Since this file serves as the "layout" for your Angular app, it's a good idea to place any shared code here, like a navbar.
- Configure your Angular app in
app.js
:
/*
* public/javascripts/app.js
*/
angular.module('sampleApp', ['ui.router']);
- Make a
templates
directory insidepublic
if you don't have one, and create a template:
➜ mkdir public/templates
➜ touch public/templates/home.html
- Configure your Angular routes in
app.js
to hook everything up:
/*
* public/scripts/app.js
*/
angular.module('sampleApp', ['ui.router'])
.config(config);
config.$inject = ['$stateProvider', '$urlRouterProvider', '$locationProvider'];
function config($stateProvider, $urlRouterProvider, $locationProvider) {
console.log('config');
//this allows us to use routes without hash params!
$locationProvider.html5Mode({
enabled: true,
requireBase: false
});
// for any unmatched URL redirect to /
$urlRouterProvider.otherwise("/");
$stateProvider
.state('home', {
url: "/",
controller: 'HomeController',
controllerAs: 'home',
template: 'Home!'
});
}
- Configure your controller with some test data, so you can check to see if the route, template, and controller are properly connected:
/*
* public/javascripts/app.js
*/
angular.module('sampleApp', ['ui.router'])
.config(config)
.controller('HomeController', HomeController);
function HomeController() {
var vm = this;
vm.homeTest = "Welcome to the homepage!";
}
- To allow
body-parser
to parse incoming JSON data, add this line toserver.js
:
/*
* server.js
*/
...
// parse form data ( application/x-www-form-urlencoded )
app.use(bodyParser.urlencoded({ extended: true }));
// parse application/json
app.use(bodyParser.json()); // ADD THIS LINE
...
Now that your Angular app is all set up, it's time to CRUD a resource! You'll need:
- CRUD routes for your resource:
/*
* server.js
*/
...
/*
* API routes
*/
app.get('/api/todos', function (req, res) {
...
});
app.post('/api/todos', function (req, res) {
...
});
app.get('/api/todos/:id', function (req, res) {
...
});
app.put('/api/todos/:id', function (req, res) {
...
});
app.delete('/api/todos/:id', function (req, res) {
...
});
/*
* Load `views/index.hbs` file
* when any route is requested from the server
*/
app.get('*', function (req, res) {
res.render('index');
});
...
- CRUD actions that render JSON:
/*
* server.js
*/
...
/*
* API routes
*/
app.get('/api/todos', function (req, res) {
Todo.find(function (err, allTodos) {
if (err) {
res.status(500).json({ error: err.message });
} else {
res.json(allTodos);
}
});
});
app.post('/api/todos', function (req, res) {
var newTodo = new Todo(req.body);
newTodo.save(function (err, savedTodo) {
if (err) {
res.status(500).json({ error: err.message });
} else {
res.json(savedTodo);
}
});
});
...
- Use the Angular
$http
or$resource
service to interact with your JSON API endpoints. See the module on ng-resource for reference. Instead of calling an external API, you're now querying your own application's server.