Angular JS single page application part 3

Continuing from part 2 of making a contact list as a Angular JS application, we need to save information in a database, we can use a REST API to store all our contacts.

angular-js-part3

The way angular interacts with REST API and database is though Angular Resource. So first we need to create the REST API on the backend to be consumed by our Angular Resource that uses this backend.

We are going to use a MongoDB database to store our data. First we are going to install mongoose npm plugin to interact with our MongoDB database.

npm install mongoose --save

Also we need body-parser plugin for handling HTTP get or post requests.

npm install body-parser --save

REST API using Node and Express js

We begin by adding a new file under root directory of our app; we are going to put our REST API in a different file.

Create a new file and names it api.js. First add required dependencies to the file.

var express = require('express'),
    mongoose = require('mongoose'),
    bodyParser = require('body-parser'),
    router = express.Router();
mongoose.connect('mongodb://localhost:27017/contacts');

As you can see we are again using express and express.Router feature. Next we create a database table schema named Contacts and check if the database table is empty then populate it with some data:

// create a model for a contact
var Contact = mongoose.model('Contact', {userId: String, firstName: String, lastName: String});
// populate schema if no data found
Contact.find().then(function (data) {
    if (data.length === 0) {
        [{userId: 1, firstName: 'john', lastName: 'smith'},
            {userId: 2, firstName: 'joy', lastName: 'smith'},
            {userId: 3, firstName: 'kate', lastName: 'smith'}].map(function (person) {
            var contact = new Contact({userId: person.userId, firstName: person.firstName, lastName: person.lastName});
            contact.save();
            console.log(contact)
        });
    }
});

Then we add two routing function, first one is “/contact” which list all our contacts and “/contact/:userId” which returns one contact detail based on the userId we pass in. Finally we export the router to be used by the server module:

router
    .use(bodyParser.json())
    .route('/contact')
    .get(function (req, res) {
        Contact.find().then(function (data, err) {
            //console.log('contacts:', data);
            res.json(data);
        });
    })
    .post(function (req) {
        var contact = new Contact(req.body);
        contact.save(function (err) {
            if (err) console.log(err);
        });
    });

router
    .route('/contact/:userId')
    .get(function (req, res) {
        console.log(req.params);
        Contact.findOne({userId: req.params.userId}).then(function (data, err) {
            //console.log('contacts:', data);
            if (data) {
                res.json(data);
            } else {
                res.json([])
            }
        });
    });

module.exports = router;

To use our new REST API we simply require the api.js file in the Server.js and add it to our express route as:

app.use('/api', api);

Server.js file now should look like this:

var express = require("express"),
    app = express(),
    path = require('path'),
    router = express.Router(),
    api = require('./api');

app.use(express.static(path.join(__dirname, 'public')));
app.use('/api', api);
router
    .get('*', function (req, res) {
        var options = {
            root: __dirname + '/public/',
            dotfiles: 'deny',
            headers: {
                'x-timestamp': Date.now(),
                'x-sent': true
            }
        };

        var fileName = "main.html";
        res.sendFile(fileName, options, function (err) {
            if (err) {
                console.log(err);
                res.status(err.status).end();
            } else {
                console.log('Sent:', fileName);
            }
        });

    });
app.use(router)
    .listen(9000);

In order for angular to talk to backend, we need to use the angular-resource. we can install angular-resource to our lib folder via bower:

bower install angular-resource

Now we include this in our main.html page:

<script src="lib/angular-resource/angular-resource.min.js"></script>

Then open app.js file under public/js/app.js so we can inject angular-resource to our angular app dependencies like:

angular.module('contactsApp',['ngRoute', 'ngResource'])

.config(function($routeProvider, $locationProvider){
    $routeProvider
        .when('/', {
        controller: 'ListController',
        templateUrl: 'views/list.html'
    });
    
    $locationProvider.html5Mode(true);
});

Now we going to create a factory to handle the service calls and returned data to the controllers.

Create a new file under public/js/ and name it factories.js then add the following into it:

angular.module('contactsApp')
    .factory('Contact', function ($resource) {
        return $resource('/api/contact/:id', {userId: '@id'}, {
            'update': {method: 'PUT'}
        })
    });

Make sure you have added new js files into the main.html file so they will load. main.html file now should look like this:

</pre>
<pre><!DOCTYPE html>
<html lang="en" ng-app="contactsApp">

<head>
    <title>Contacts</title>
    <link rel="stylesheet" href="lib/bootstrap/dist/css/bootstrap.min.css"/>
    <base href="/">
</head>

<body>
<div class="container">
    <div class="page-header">
        <h1>Contacts</h1>
    </div>
    <div class="row">
        <div class="col-sm-12" ng-view>
        </div>
    </div>
</div>
</body>
<script src="lib/jquery/dist/jquery.min.js"></script>
<script src="lib/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="lib/angular/angular.min.js"></script>
<script src="lib/angular-route/angular-route.min.js"></script>
<script src="lib/angular-resource/angular-resource.min.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
<script src="js/factories.js"></script>

</html></pre>
<pre>

Modify the controllers.js file to inject the Contact via resource factory and add it to the angular $scope this will make sure the contact model is available in our list.html file:

angular.module('contactsApp')
    .controller('ListController', function ($scope, Contact) {
        $scope.contacts = Contact.query();
        console.log($scope.contacts);
        $scope.fields = ['firstName', 'lastName'];
        // add sorting function
        $scope.sort = function (field) {
            $scope.sort.field = field;
            $scope.sort.order = !$scope.sort.order;
        };

        $scope.sort.field = 'firstName';
        $scope.sort.order = false;
    });

Finally we can modify the list.html file to actually render the data from our contact model.

</pre>
<pre><p><input type="search" class="form-control" placeholder="Search" ng-model="query"/></p>
<table class="table table-hover table-bordered">
    <tr>
        <th ng-repeat="field in fields" ng-click="sort(field)">
            {{field}}
            <span class="glyphicon glyphicon-chevron-down" ng-show="sort.field === field && !sort.order"></span>
            <span class="glyphicon glyphicon-chevron-up" ng-show="sort.field === field && sort.order"></span>
        </th>
    </tr>
    <tr ng-repeat="contact in contacts | filter: query | orderBy: sort.field : sort.order">
        <td ng-repeat="field in fields">
            {{ contact[field]}}
        </td>
    </tr>
</table></pre>
<pre>

Looking at above code, you can see how easy it is to populate tables using ng-repeat and even use angular built-in sorting and search functionality. Run the server and you should see a list of contacts by first name and last name and sorting, searching functionality by simply clicking on the headers.

You can find a copy of the source code on the github:

https://github.com/molasaheb/ContactsProject/tree/Part3

if you liked this tutorial please share it on social media.

in the next part we will learn about more advance data manipulation using node.js and REST API.

Author: Ali Shirzad

Share This Post On

1 Comment

  1. thanks for this tut, very usefull for me

    Post a Reply

leave a comment

%d bloggers like this: