AngularJS example MotoAds with NodeJS and MongoDB

I built MotoAds demo application in AngularJS but it did not have any server layer. Data was read directly from the json files. So I was decided to build the server side services. I know pretty well JEE and relational database, so I could use it. But I want to know something new so I chose NodeJS nad MongoDB. Thanks to this decision I got a full stack JavaScript application. It's incredible to use JavaScript to build the complete application.

Now MotoAds demo application consists of:
  • User interface in AngularJS and Bootstrap with full CRUD operations: add, read, edit and remove adverts.
  • Server service layer was built in NodeJS with RESTful serrvices. To simplify the use of a NodeJS I used ExpressJS.
  • Database: all data except the pictures are stored in MongoDB.
So let's see how to add the CRUD operations in AngularJS with services in NodeJS with MongoDB.

Step 1
We write in services.js access to our RESTful services:
'use strict';

var motoAdsServices = angular.module('motoAdsServices', ['ngResource']);

motoAdsServices.factory('Brand', ['$resource', function($resource) {
    return $resource('/api/brands', {}, {});
  }]);

motoAdsServices.factory('Country', ['$resource', function($resource) {
    return $resource('/api/countries', {}, {});
  }]);

motoAdsServices.factory('Advert', ['$resource', function($resource) {
    return $resource('/api/adverts/:advertId', {}, {
       update: {method:'PUT', params: {advertId: '@_id'}}
    });
  }]);

Step 2
Inject services in app.js:
'use strict';

var motoAdsApp = angular.module('motoAdsApp', ['ngRoute', 'ui.bootstrap', 'motoAdsServices']);

motoAdsApp.config(['$routeProvider',
  function($routeProvider) {
    $routeProvider.
            when('/', {
              controller: 'AdvertsController',
              templateUrl: 'views/adverts.html'
            }).
            when('/addAdvert', {
              controller: 'AddAdvertController',
              templateUrl: 'views/addAdvert.html'
            }).
            when('/editAdvert/:advertId', {
              controller: 'EditAdvertController',
              templateUrl: 'views/editAdvert.html'
            });
  }]);

We use the angularjs services in controllers.js:
motoAdsApp.controller('AdvertsController', ['$scope', '$window', 'Brand', 'Country', 'Advert',
  function($scope, $window, Brand, Country, Advert) {
    // ...
    $scope.brands = Brand.query();

    $scope.countries = Country.query();
    // ...
    $scope.adverts = [];
    var allAdverts = Advert.query(filterAdverts);
    
    // ...
    $scope.$watch('filter', filterAdverts, true);
    
    function filterAdverts() {
      $scope.adverts = [];
      angular.forEach(allAdverts, function(row) {
        // ...
        $scope.adverts.push(row);
      });
    }

    $scope.removeAdvert = function(idx) {
      var removeAdvert = $scope.adverts[idx];
      Advert.remove({advertId: removeAdvert._id}, function() {
        $scope.adverts.splice(idx, 1);
        alert('Advert removed');
      });
    };

    $scope.editAdvert = function(_advertId) {
      $window.location = "#/editAdvert/" + _advertId;
    };

  }]);

motoAdsApp.controller('AddAdvertController', ['$scope', '$window', 'Brand', 'Country', 'Advert',
  function($scope, $window, Brand, Country, Advert) {
    $scope.brands = Brand.query();

    $scope.countries = Country.query();

    // ...

    $scope.addAdvert = function() {
      Advert.save($scope.newAdvert, function() {
        alert('New advert added');
        $window.location = "#/";
      });
    };

    // ...
  }]);

motoAdsApp.controller('EditAdvertController', ['$scope', '$routeParams', '$window', 'Brand', 'Country', 'Advert',
  function($scope, $routeParams, $window, Brand, Country, Advert) {
    $scope.brands = Brand.query();

    //...

    $scope.countries = Country.query();

    // ...

    var previousAdvert = null;
    $scope.editAdvert = Advert.get({advertId: $routeParams.advertId}, function() {
      // ...
    });

    $scope.updateAdvert = function() {
      Advert.update($scope.editAdvert, function() {
        alert('Advert updated');
        $window.location = "#/";
      });
    };

    // ...
  }]);

Step 3
In server.js we create the HTTP server with the RESTful service in NodeJS and ExpressJS. It is also used to host angularjs app:
var express = require('express');
var path = require('path');
var http = require('http');
var brands = require('./routes/brands');
var countries = require('./routes/countries');
var adverts = require('./routes/adverts');

var app = express();

app.configure(function() {
  app.set('port', process.env.PORT || 3000);
  app.use(express.logger('dev'));  /* 'default', 'short', 'tiny', 'dev' */
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(express.static(path.join(__dirname, 'public')));
  // TODO should be created separate test server
  app.use('/test', express.static(path.join(__dirname, 'test')));
});

app.get('/api/brands', brands.findAll);
app.get('/api/countries', countries.findAll);

app.get('/api/adverts', adverts.findAll);
app.get('/api/adverts/:id', adverts.findById);
app.post('/api/adverts', adverts.add);
app.put('/api/adverts/:id', adverts.update);
app.delete('/api/adverts/:id', adverts.remove);

http.createServer(app).listen(app.get('port'), function() {
  console.log("Express server listening on port " + app.get('port'));
});

Step 4
In adverts.js we write some code in NodeJS which allows us to use MongoDB:
var mongo = require('mongodb');

var Server = mongo.Server;
var Db = mongo.Db;
var BSON = mongo.BSONPure;

var server = new Server('localhost', 27017, {auto_reconnect: true});
db = new Db('motoads', server);

db.open(function(err, db) {
  if (!err) {
    console.log("Connected to motoads database");
    db.collection('adverts', {strict: true}, function(err, collection) {
      if (err) {
        console.log("The adverts collection does not exist. Creating it with sample data...");
        populateDB();
      }
    });
  }
});

exports.findAll = function(req, res) {
  db.collection('adverts', function(err, collection) {
    collection.find().toArray(function(err, items) {
      console.log('adverts send from DB');
      res.send(items);
    });
  });
};

exports.findById = function(req, res) {
  var id = req.params.id;
  console.log('Retrieving advert: ' + id);
  db.collection('adverts', function(err, collection) {
    collection.findOne({'_id': new BSON.ObjectID(id)}, function(err, item) {
      res.send(item);
    });
  });
};

exports.add = function(req, res) {
  var advert = req.body;
  console.log('Adding advert: ' + JSON.stringify(advert));
  db.collection('adverts', function(err, collection) {
    collection.insert(advert, {safe: true}, function(err, result) {
      if (err) {
        res.send({'error': 'An error has occurred'});
      } else {
        console.log('Success: ' + JSON.stringify(result[0]));
        res.send(result[0]);
      }
    });
  });
};

exports.update = function(req, res) {
  var id = req.params.id;
  var advert = req.body;
  console.log('Updating advert: ' + id);
  console.log(JSON.stringify(advert));
  delete advert._id;
  db.collection('adverts', function(err, collection) {
    collection.update({'_id': new BSON.ObjectID(id)}, advert, {safe: true}, function(err, result) {
      if (err) {
        console.log('Error updating advert: ' + err);
        res.send({'error': 'An error has occurred'});
      } else {
        console.log('' + result + ' document(s) updated');
        res.send(advert);
      }
    });
  });
};

exports.remove = function(req, res) {
  var id = req.params.id;
  console.log('Removing advert: ' + id);
  db.collection('adverts', function(err, collection) {
    collection.remove({'_id': new BSON.ObjectID(id)}, {safe: true}, function(err, result) {
      if (err) {
        res.send({'error': 'An error has occurred - ' + err});
      } else {
        console.log('' + result + ' document(s) removed');
        res.send(req.body);
      }
    });
  });
};

var populateDB = function() {
  var fs = require('fs');
  var file = './data/adverts.json';

  fs.readFile(file, 'utf8', function(err, data) {
    if (err) {
      throw err;
    }
    var adverts = JSON.parse(data);
    db.collection('adverts', function(err, collection) {
      if (err) {
        throw err;
      }
      collection.insert(adverts, {safe: true}, function(err, result) {
        if (err) {
          throw err;
        }
      });
    });
  });
};

We run MongoDB and start our HTTP server (node server.js). Now we are typing in web browser URL http://localhost:3000/#/. In web browser we should see the MotoAds application and we can use CRUD operations.

If you want to run this example on your computer, you can download sources from GitHub.

Any comment would be highly appreciated.

Komentarze

Popularne posty z tego bloga

Java ESL program for connecting to the FreeSWITCH

AngularJS example MotoAds more advanced than the tutorial on angularjs.org

Java program for connecting to the FreeSWITCH XML-RPC