April 13, 2016

A simple CRUD application using MEAN and mongoose

A simple CRUD application using MEAN and mongoose

I am going to start explaining this with an example of creating a phonebook app using MongoDB, Angular.js, Express.js, Node.js and mongoose. Where you can create, view, delete and update a contact.

Initially when i started learning about MEAN stack, I came across many websites and did not find in one particular site according to my needs. I surfed many websites to find my solution. Each solution was in different websites. So, i planned to write about it here, all in one place. Let's break this into steps.

What is MEAN?

MEAN stands for MongoDB, Express.js, Angular.js, Node.js

What is MongoDB?

MongoDB is the leading NoSQL database, empowering businesses to be more agile and scalable.

What is Express?

Express is a minimal and flexible node.js web application framework, providing a robust set of features for building single and multi-page, and hybrid web applications.

What is Angular.js?

AngularJS lets you extend HTML vocabulary for your application. The resulting environment is extraordinarily expressive, readable, and quick to develop.

What is Node.js?

Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications.

What all i am going to explain in this post?

  1. Creating a project folder with necessary packages
  2. Creating angular route, partials and controllers
  3. Creating mongo db
  4. Creating mongoose Schema
  5. Create the user
  6. View the user
  7. Delete the user
  8. update the user

Creating a project folder with necessary packages

Let's start by creating a project folder and installing the various packages required for our project. Let me create a folder name PhoneBookApp.

Now lets install various packages that are necessary for creating our app. Before that, you should install node and npm to your PC. Installing node and npm is explained in detail here. And using npm we have to install bower. It is explained in detail here.

After installing node and bower, we can start installing the node and bower components required. Our node-modules include express, body-parser and mongoose. Our bower-components include jquery, bootstrap, angular, angular-route, toastr and font-awesome

Here i am using bootstrap for styling, toastr for notification and font-awesome for icons. You can neglect this if you want.

Application Structure

--app
-----config
--------mongoose.js (setting up mongo db)
-----controller
--------AddCtrl.js (angular controller for add contact)
--------EditCtrl.js (angular controller for edit contact)
--------HomeCtrl.js (angular controller for home page)
--------ListCtrl.js (angular controller for list contact)
--------ViewCtrl.js (angular controller for view contact)
-----css
-----img
-----libs (created by bower install)
-----model
--------contact.js (mongoose schema to handle CRUD)
-----route
--------contact.js (service calls)
--------route.js (page routing)
-----view
--------add.html (html template for add contact)
--------edit.html (html template for edit contact)
--------home.html (html template for home page)
--------list.html (html template for list contact)
--------view.html (html template for view contact)
-----app.js
-----index.html
---node-modules (created by npm install)
---.bowerrc (tells bower where to put files "app/libs")
---bower.json (tells bower which files we need)
---package.json (tells npm which packages we need)
---server.js (set up our node application)

Setting Up Our Node Application Server

The service file contains the port for which the localhost runs. Here you can specify the default location of the app. The mongoose, model schema and routes will be imported here.

server.js

var express = require('express'),
    bodyParser = require('body-parser');

var app = express();
app.use(express.static(__dirname + '/app'));
app.use(bodyParser.json());
.
.
.
app.get('*', function(req, res) {
    res.sendfile('./app/index.html');
});

var port = "3000";
app.listen(port);
console.log("Magic happens at " + port);

See how i have pulled in the module express.

The default html page contains the scripts snd the styles that are required. The name of your app is defined in the ng-app directive.

index.html

<html ng-app="app">
   <body>
      <div ng-view></div>
   </body>
</html>

The templates inside view folder gets embedded into ng-view when routing happens.

Routing

Routing is explained deeply here, you can take a look at it.

route.js

angular.module('app').config(function($locationProvider, $routeProvider) {
   $locationProvider.html5Mode(true);

   $routeProvider
      .when('/', {
         templateUrl: 'view/home.html',
         controller: 'HomeController'
      })
      .when('/add', {
         templateUrl: 'view/add.html',
         controller: 'AddController'
      })
      .when('/list', {
         templateUrl: 'view/list.html',
         controller: 'ListController'
      })
      .when('/view/:id', {
         templateUrl: 'view/view.html',
         controller: 'ViewController'
      })
      .when('/edit/:id', {
         templateUrl: 'view/edit.html',
         controller: 'EditController'
      })
});

When /add is clicked in index.html file, the add.html template inside view folder gets embedded into ng-view with controller AddController. Same way the rest of the routing works. /view/:id means when a particular contact is clicked the :id gets the param id of the contact and get appended to it.

View and Controller

Let me show you each of my template and controller files, how i have written it.

add.html

<input placeholder="First Name" ng-model="contact.fname">
<input placeholder="Last Name" ng-model="contact.lname">
<button ng-click="save()">Save</button>

See how i have defined my ng-model. When the save button is clicked the save() function gets triggered in the AddCtrl.js controller.

AddCtrl.js Controller

$scope.save = function() {
   $http.post('/api', $scope.contact).success(function(data) {
      $scope.contact = data;
      $location.path('/list');
   })
}

The save function contains the service method post which adds a new contact to the database.

When adding contact is successful i have redirected to list.html where the list of all the added contacts are shown.

list.html

<div ng-repeat="contact in contacts" ng-click="view(contact._id)">
    <div>{{contact.fname}}</div>
    <div>{{contact.lname}}</div>
</div>

Here the ng-repeat directive used to repeat the contacts.

Onclick of each contact the view() method is triggered and the routing takes place and details of the particular contact alone is displayed.

ListCtrl.js Controller

$http.get('/api').success(function(data) {
   $scope.contacts = data;
});

$scope.view = function(id) {
   $location.path('/view/' + id);
};

The get service method gets all the contacts that has been added.

when the view(contact._id) method is clicked the view method gets the contact._id parameter and that id is appended to the url when the routing is taking place. The routing is done using the $location service.

view.html

<div>{{contact.fname}}</div>
<div>{{contact.lname}}</div>
<button ng-click="edit()">Edit</button>
<button ng-click="delete()">Delete</button>

Here the detail of the particular contact is displayed.

ViewCtrl.js Controller

var id = $routeParams.id;
$http.get('/api/' + id).success(function(data) {
   $scope.contact = data;
});

$scope.delete = function() {
   $http.delete('/api/' + id).success(function(data){
      $scope.contact = data;
      $location.path('/list');
   });
};

$scope.edit = function() {
   $location.path('/edit/' + id);
}

The $routeParams.id fetches the id parameter. The get service method gets the detail of the particular contact clicked. There are two buttons shown. One for editing contact and another for deleting contact. When the delete button is clicked the delete() method gets triggered. The delete service method deletes the particular contact. When the edit button is clicked the edit() function gets triggered and it redirects to the edit.html page.

edit.html

<input placeholder="First Name" ng-model="contact.fname">
<input placeholder="Last Name" ng-model="contact.lname">
<button ng-click="update()">Save</button>

The edit.html is same as the add.html page. Only difference is the input field gets filled up with the data of the particular contact so that when the changes are made and the update button is clicked the data gets updated to the database. Whereas in add.html page when the save button is clicked a new contact has been added to the database.

EditCtrl.js Controller

var id = $routeParams.id;
$http.get('/api/' + id).success(function(data) {
   $scope.contact = data;
});

$scope.update = function() {
   $http.put('/api/' + $scope.contact._id, $scope.contact).success(function(data) {
      $scope.contact = data;
      $location.path('/list');
   });
};

Same like before the get service method gets the contact details of the particular contact using the id of the contact. The put service method puts the edited contact data to the existing data in the database without creating a newer one. And on success of this it redirects to the list.html page.

Now the templates and the controllers are ready, now let me define a mongoose schema.

Mongoose Schema

model/contact.js

var mongoose = require('mongoose');

var contactSchema = mongoose.Schema({
   fname: String,
   lname: String
});

var Contact = mongoose.model('Contact', contactSchema);

First, you included your model dependencies and then you used the Mongoose Schema object to create a new contactSchema.

fname is a string field that represents the contacts first name. lname is a string field that represents the contacts last name.

In the end, you registered the Contact Mongoose model to allow you to use it in the Contact Express controller.

Next, you'll need to make sure your application is loading the model file, so go back to the config/mongoose.js file and change it as follows:

mongoose.js

var mongoose = require('mongoose'),
    contactModel = require('../model/contact');

mongoose.connect('mongodb://localhost/contactdb');

This will load your new model file and make sure your application can use your Contact model. It also contains the connection of MongoDB and contactdb database is been created.

Express Controllers and Services

Now lets start writing the express controllers and services.

Adding a contact

app.post('/api', function(req, res, next) {
   var contact = new Contact({
      fname: req.body.fname,
      lname: req.body.lname
   });
   contact.save(function(err, data) {
      if(err) {
         return next(err);
      }
      res.status(201).json(data);
   });
});

Viewing the list of contacts

app.get('/api', function(req, res, next) {
   Contact.find().exec(function(err, data) {
      if(err) {
         return next(err);
      }
      res.json(data);
   });
});

Viewing a particular contact

app.get('/api/:id', function(req, res) {
   Contact.findById(req.params.id, function(err, data){
      res.json(data);
   });
});

Updating a contact

app.put('/api/:id', function(req, res, next) {
   Contact.findById(req.params.id, function(err, data) {
      data.fname = req.body.fname;
      data.lname = req.body.lname;
      data.updated = Date.now();
      data.save(function(err, data) {
         if(err) {
             return next(err);
         }
         res.status(201).json(data);
      });
   });
});

Deleting a contact

app.delete('/api/:id', function(req, res) {
   Contact.findByIdAndRemove(req.params.id, function(err, data) {
      res.json(data);
   });
});

That's it your done creating a simple app using MEAN stack.

Fork the entire code from github and say thanks,

https://github.com/vivinantony/PhoneBookApp

4 comments:

  1. Its Good to Learn.





























    Its Good to learn about MEAN stack CRUD app.
    Thanks.


    ReplyDelete
  2. i think the spelling mistake in the sentence that located above the index.html. The word is scipts. I think its a word is scripts

    ReplyDelete
    Replies
    1. Thank you Barath. Have corrected it :)

      Delete
    2. Welcome Vivin. All the best for everything.

      Delete

Popular Posts

Views