- Setting up the Application - Make sure to update your application's
app.js to include the newly created app_client directory:
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'app_client')));
- Add sending of index.html to the browser - In app.js you need to send your front-end
index.html file that includes HTML and Angular directives to the browser. That is performed with
the following code:
app.use('/api', routesApi);
// Added per Lab 5 - Angular
app.use(function(req, res) {
res.sendFile(path.join(__dirname, 'app_client', 'index.html'));
});
- Creating the Angular app file - An Angular application file must
be developed and placed in your app_client directory. You can use a
file name of app.js, but a more application specific file name
might be more wise, say for example bloggerApp.js. Within that
file you hve to declare your Angular module, as so:
var app = angular.module('bookApp', []);
- Download and use Angular.js files - At a miniumum, your SPA will
require angular.min.js and angular-route.min.js. Find these
from a reputable sourde and download them to the lib directory of
your app_client directory for use.
- Add an Angular Route Provider - The
ngRoute
module provides the basis
for a router that will map your applications screens to different controllers and HTML templates. See section 9.2.2 of the book.
Example:
var app = angular.module('bookApp', ['ngRoute']);
//*** Router Provider ***
app.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'pages/home.html',
controller: 'HomeController',
controllerAs: 'vm'
})
.when('/book-list', {
templateUrl: 'pages/book-list.html',
controller : 'ListController',
controllerAs: 'vm'
})
.when('/book-add', {
templateUrl: 'pages/book-add.html',
controller: 'AddController',
controllerAs: 'vm'
})
.when('/book-edit/:id', {
templateUrl: 'pages/book-edit.html',
controller: 'EditController',
controllerAs: 'vm'
})
.otherwise({redirectTo: '/'});
});
- Adding Views, Controllers and Services - Now, this is where the book gets complicated and the examples and sample code a bit fuzzy. You can develop your own code without services, if you
wish, to keep it simple. Regardless, you will have to learn Angular directives that go in
your HTML file(s), ng-view, ng-repeat, ng-submit, etc. The controllers act and work much
like the controllers written under Express.
- Use ViewModels - In your controllers, use the ControlAs attribute and define a
ViewModel and name it
vm
. The ViewModel is an object that passes between the
controller code and your HTML page code and maintains state and information bindings.
- Services, Directives and Filters...oh my! - Please read the book to better understand
these facilites and use them in your labs if you desired (though they are NOT required).
- IIFE and uglify, etc - The book describes and shows the use of IIFE and uglify. These
are good for production code and larger applications, but not required for the labs you will
be developing.
- $http - The $http client module is all that is needed for executing a REST API, and
it supports get, post, put and delete. It is rather simple to use (see the book and on-line examples). The one thing to be wary of is that the client object itself must be pasaed into your controller
methods in order to be accessed.
- Example - An example method and its use in a Controller:
//*** REST Web API functions ***
function getAllBooks($http) {
return $http.get('/api/books');
}
function getBookById($http, id) {
return $http.get('/api/books/' + id);
}
function updateBookById($http, id, data) {
return $http.put('/api/books/' + id, data);
}
. . .
//*** Controllers ***
app.controller('ListController', function ListController($http) {
var vm = this;
vm.pageHeader = {
title: 'Book List'
};
getAllBooks($http)
.success(function(data) {
vm.books = data;
vm.message = "Book data found!";
})
.error(function (e) {
vm.message = "Could not get list of books";
});
});
app.controller('EditController', [ '$http', '$routeParams', '$state', function EditController($http, $routeParams, $state) {
var vm = this;
vm.book = {}; // Start with a blank book
vm.id = $routeParams.id; // Get id from $routParams which must be injected and passed into controller
vm.pageHeader = {
title: 'Book Edit'
};
// Get book data so it may be displayed on edit page
getBookById($http, vm.id)
.success(function(data) {
vm.book = data;
vm.message = "Book data found!";
})
.error(function (e) {
vm.message = "Could not get book given id of " + vm.id;
});
// Submit function attached to ViewModel for use in form
vm.submit = function() {
var data = vm.book;
data.bookTitle = userForm.bookTitle.value;
data.bookText = userForm.bookText.value;
updateBookById($http, vm.id, data)
.success(function(data) {
vm.message = "Book data updated!";
$state.go('book-list'); // Refer to book for info on StateProvder
})
.error(function (e) {
vm.message = "Could not update book given id of " + vm.id + userForm.bookTitle.text + " " + userForm.bookText.text;
});
}
}]);
- HTML code - The HTML for an Angular SPA is relatively straightforward and for most simple apps all
the HTML code will be in one file, index.html in the app_client directory, and that file will
have your navigation and an Angular template for each 'page' of your app. Take particular note of the bold
HTML tags below and recognize that the ng-view directive in one div is critical to bind your
page to your route provider:
<!doctype html>
<html ng-app="bookApp">
<head>
<script src="/js/angular.min.js"></script>
<script src="/js/angular-route.min.js"></script>
<script src="/js/angular-ui-router.min.js"></script>
<link rel='stylesheet' href='/stylesheets/style.css' />
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/fontawesome-all.css">
</head>
<body>
<!-- Navigation -->
<nav class="navbar fixed-top navbar-expand-sm navbar-dark bg-primary">
<a class="navbar-brand" href="#/"My Books</a>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-item nav-link active" href="#/book-list"List Books</a>
a class="nav-item nav-link active" href="#/book-add"Add Books</a>
</div>
</div>
</nav>
<!-- Angular Templates -->
<script type="text/ng-template" id="pages/home.html">
<p></p>
<h2>{{vm.pageHeader.title}}</h2>
<h4>{{vm.message}}</h4>
</script>
<script type="text/ng-template" id="pages/book-list.html">
<p></p>
<div ng-repeat="book in vm.books">
<div class="card border-primary" style="width: 50%">
<div class="card-header">
</div>
<div class="card-body">
<h5 class ="card-title">{{book.bookTitle}</h5>
</div>
<div class="card-footer">
<div class = "col-sm">
<ul class="nav justify-content-end nav-pills card-header-pills">
<li class="nav-item">
<a class="nav-link" href="#/book-edit/{{book._id}}" <i class="fas fa-edit"</i></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#/book-delete/{{book._id}}" <i class="fas fa-trash-alt"></i></a>
</li>
</ul>
</div>
</div>
</div>
<p>
</div>
</script>
<script type="text/ng-template" id="pages/book-edit.html">
<p></p>
<div class="card border-primary" style="width: 50%">
<form name="userForm" ng-submit="vm.submit()" ng-controller="EditController" style="padding: 20px">
<div class="form-group">
<label for="summary" class="text-primary">Book Title</label>
<input class="form-control" id="bookTitle" name="bookTitle" value="{{vm.book.bookTitle}}">
<label for="detail" class="text-primary">Book Text</label>
<textarea class="form-control" id="bookText" name="bookText" rows="3">{{vm.book.bookText}}</textarea>
</div>
<input type="submit" class="btn btn-primary" value="Save Book">
</form>
</div>
</script>
<!-- Angular View (dynamic content goes here) -->
<div ng-view></div>
<script src="/js/bookApp.js"></script>
</body>
</html>