Creating Real-time Chat App with Nodejs, Express, Socket and MongoDB

Creating Real-time Chat App with Nodejs, Express, Socket and MongoDB

In this tutorial, we’ll be building a real-time chat application with NodeJS, Express, Socket.io, and MongoDB.

How to Build a Real-time Chat App With NodeJS, Socket.IO, and MongoDB

In this post, we’ll be building a real-time chat application with NodeJS, Express, Socket.io, and MongoDB.

Here is a screenshot of what we’ll build:

Setup

I’ll assume that you already have NodeJS and NPM installed. You can install it from the Node.js website if you don’t have it installed already.

A basic Knowledge of Javascript is required.

Let’s get started.

Create a directory for the application, open the directory with your favourite editor such as Visual Studio Code. You can use any other editor, I’ll be using VS code in this tutorial:

mkdir chatApplication && cd chatApplication && code .

Next, let’s initialize the directory as a Nodejs application.

npm init

You’ll be prompted to fill in some information — that’s okay. The information will be used to set up your package.json file.

Dependencies Installation

Let’s install our application’s dependencies.

We’ll be using the express web server to serve our static files and body-parserextract the entire body portion of an incoming request stream and exposes it to an API endpoint. So, let's install them. You'll see how they are used later in this tutorial.

npm install express body-parser --save

We added the — save flag so that it’ll be added as a dependency in our package.json file.

Note:

Please, don’t use express generator as I won’t cover how to configure _socket.io_ to work with express generator setup.

Next, install the mongoose node module. It is an ODM (Object Document Mapper) for MongoDB and it’ll make our job a lot easier.

Let’s install it alongside socket.io and bluebird. Socket.IO is a JavaScript library for real-time web applications. Bluebird is a fully-featured Promise library for JavaScript.

npm install mongoose socket.io bluebird --save

That’s it for the Nodejs backend module installation.

Our package.json file should look like this now.

Another way to install the above packages is to copy the package.json file above and paste it into your package.json file and run:

npm install

It’ll install all the required packages.

Let’s set up the client side.

To connect Socket.IO server to the client we add the Socket.IO client-side javascript library.

<script  src="/js/socket.js"></script>

That will be our HTML file for the frontend. You can grab the entire code for the frontend here to follow along. The best way to learn is to follow along.

You can download the client-side socket.io library here as well.

And here /js/chat.js is where we’ll have our custom client-side javascript code.

Setting up our express server:

Create an App.js. You can call it server.js if you like.
It’s my personal preference to call it App.js.

Inside the App.js file let’s create and configure the express server to work with socket.io.

App.js

This is the basic configuration required to set up socket.io in the backend.

Socket.IO works by adding event listeners to an instance of http.Server
which is what we are doing here:

const socket = io(http);

Here is where we listen to new connection events:

socket.on(“connection”, (socket)=>{
console.log(“user connected”);
});

For example, if a new user visits localhost:500 the message “user connected” will be printed on the console.

socket.on() takes an event name and a callback as parameters.

And there is also a special disconnect event that gets fire each time a user closes the tab.

socket.on(“connection”, (socket)=>{
    console.log(“user connected”);
    socket.on("disconnect", ()=>{
    console.log("Disconnected")
})
});
Setting up our frontend code

Open up your js/chat.js file and type the following code:

(function() {
    var  socket  =  io();
    $("form").submit(function(e) {
        e.preventDefault(); // prevents page reloading
        socket.emit("chat message", $("#m").val());
        $("#m").val("");
    return  true;
});
})();

This is a self-executing function it initializes socket.io on the client side and emits the message typed into the input box.

With this line of code, we create a global instance of the soicket.io client on the frontend.

var  socket  =  io();

And inside the submit event handler, socket io is getting our chat from the text box and emitting it to the server.

$("form").submit(function(e) {
    e.preventDefault(); // prevents page reloading
    socket.emit("chat message", $("#m").val());
    $("#m").val("");
 return  true;
});

If you’ve gotten to this point, congratulations, you deserve some accolades.

Great, we have both our express and socket.io server set up to work well. In fact, we’ve been able to send messages to the server by emitting the message from our input box.

socket.emit("chat message", $("#m").val());

Now from the server-side let’s set up an event to listen to the “chat message” event and broadcast it to clients connected on port 500.

App.js

socket.on("chat message", function(msg) {

console.log("message: "  +  msg);

//broadcast message to everyone in port:5000 except yourself.

socket.broadcast.emit("received", { message: msg  });
   });
});

This is the event handler that listens to the “chat message” event and the message received is in the parameter passed to the callback function.

socket.on("chat message", function(msg){
});

Inside this event, we can choose what we do with the message from the client — -insert it into the database, send it back to the client, etc.

In our case, we’ll be saving it into the database and also sending it to the client.

We’ll broadcast it. That means the server will send it to every other person connected to the server apart from the sender.

So, if Mr A sends the message to the server and the server broadcasts it, Mr B, C, D, etc will receive it but Mr A won’t.

We don’t want to receive a message we sent, do we?

That doesn’t mean we can’t receive a message we sent as well. If we remove the broadcast flag we’ll also remove the message.

Here is how to broadcast an event:

socket.broadcast.emit("received",{message:msg})

With that out of the way, we can take the message received and append it to our UI.

If you run your application. You should see something similar to this. Please, don’t laugh at my live chat.

Wawu! Congratulations once again. let’s add some database stuff and display our chats on the frontend.

Database Setup

Install MongoDB

Visit the MongoDB website to download it if you have not done so already.

And make sure your MongoDB server is running. They have an excellent documentation that details how to go about setting it up and to get it up and running. You can find the doc here.

Create Chat Schema

Create a file in the model’s directory called models/ChatSchema.js
Nothing complex, we are just going to have 3 fields in our schema --- a message field, a sender field and a timestamp.

The ChatSchema.js file should look like this:

Connection to the MongoDB database

Create a file and name it dbconnection.js. That's where our database connection will live.

const  mongoose  = require("mongoose");
mongoose.Promise  = require("bluebird");

const  url  =  "mongodb://localhost:27017/chat";
const  connect  =  mongoose.connect(url, { useNewUrlParser: true  });

module.exports  =  connect;
Insert messages into the database

Since we are going to insert the messages in the server-side we’ll be inserting the messages we receive from the frontend in the App.js file.

So, let’s update the App.js file.

...
//database connection

const  Chat  = require("./models/Chat");
const  connect  = require("./dbconnect");

We can now add the

//setup event listener
socket.on("connection", socket  =>  {
    console.log("user connected");
    socket.on("disconnect", function() {
		
    console.log("user disconnected");
    });  
    socket.on("chat message", function(msg) {
        console.log("message: "  +  msg);
        //broadcast message to everyone in port:5000 except yourself.
    socket.broadcast.emit("received", { message: msg  });
		
    //save chat to the database
    connect.then(db  =>  {
    console.log("connected correctly to the server");
		
    let  chatMessage  =  new Chat({ message: msg, sender: "Anonymous"});
    chatMessage.save();
    });
    });
});

We are creating a new document and saving it into the Chat collection in the database.

let  chatMessage  =  new Chat({ message: msg, sender: "Anonymous"});
    chatMessage.save();
Display messages on the frontend

We’ll, first of all, display our message history from the database and append all messages emitted by events.

To achieve this, we need to create an API that sends the data from the database to the client-side when we send a get request.

const  express  = require("express");
const  connectdb  = require("./../dbconnect");
const  Chats  = require("./../models/Chat");

const  router  =  express.Router();

router.route("/").get((req, res, next) =>  {
        res.setHeader("Content-Type", "application/json");
        res.statusCode  =  200;
        connectdb.then(db  =>  {
            Chats.find({}).then(chat  =>  {
            res.json(chat);
        });
    });
});

module.exports  =  router;

In the above code, we query the database and fetch all the messages in the Chat collection.

We’ll import this into the server code App.js file and we'll also import the bodyparser middleware as well.


const bodyParser = require(“body-parser”);
const chatRouter = require(“./route/chatroute”);

//bodyparser middleware
app.use(bodyParser.json());

//routes
app.use(“/chats”, chatRouter);

With this out of the way, we are set to access our API from the frontend and get all the messages in our Chat collection.

So, we got the messages using the fetch API and we appended the messages to the UI.

You’ll also notice that I used formatTimeAgo(data.createdAt)); that is a 1.31kb library I created to manage dates for small projects since moment.js sometimes is rather too big. formatTimeAgo() will display “few seconds ago”, etc. If you are interested, you can find more information here.

Everything seems good at this point, right?

However, since you are not receiving the messages sent to the server back to yourself, let’s grab our own message from our input box and display it on the UI.


(function() {
$(“form”).submit(function(e) {

let li = document.createElement(“li”);

e.preventDefault(); // prevents page reloading

socket.emit(“chat message”, $(“#message”).val());

messages.appendChild(li).append($(“#message”).val());

let span = document.createElement(“span”);

messages.appendChild(span).append(“by “ + “Anonymous” + “: “ + “just now”);

$(“#message”).val(“”);

return false;

  });
})();

And also if we receive messages from the event let’s also output it to the UI.



(function(){

socket.on("received", data => {

let li = document.createElement("li");

let span = document.createElement("span");

var messages = document.getElementById("messages");

messages.appendChild(li).append(data.message);

messages.appendChild(span).append("by " + "anonymous" + ": " + "just now");

});
`
})

Our application is complete now. Go ahead an test it.

Note that if we had our users logged in we wouldn’t have hardcoded the “anonymous” user as it’s in our code right now. We’ll get it from the server.

And also if you want to tell everyone that someone is typing you can also add this code in the frontend.


//isTyping event
messageInput.addEventListener(“keypress”, () => {
socket.emit(“typing”, { user: “Someone”, message: “is typing…” });
});

socket.on(“notifyTyping”, data => {
typing.innerText = data.user + “ “ + data.message;
console.log(data.user + data.message);
});

//stop typing
messageInput.addEventListener(“keyup”, () => {
socket.emit(“stopTyping”, “”);
});

socket.on(“notifyStopTyping”, () => {
typing.innerText = “”;});
`

What it does is that when a user is typing it emits an event to the server and the server broadcasts it to other clients. You listen to the event and update the UI with the message “Someone is typing…” You can add the person’s name if you wish.

Here is the server-side event listener and emitter:

Congratulations.

You can improve this code, add authentication, add groups or make it a one to one chat, re-model the schema to accommodate all of that, etc.

I’ll be super excited to see the real-time applications you’ll build with socket.IO.

I hope this was helpful. The entire code is on Github. You can get it here.

Thank you for reading !

MEAN Stack Tutorial MongoDB, ExpressJS, AngularJS and NodeJS

MEAN Stack Tutorial MongoDB, ExpressJS, AngularJS and NodeJS

We are going to build a full stack Todo App using the MEAN (MongoDB, ExpressJS, AngularJS and NodeJS). This is the last part of three-post series tutorial

We are going to build a full stack Todo App using the MEAN (MongoDB, ExpressJS, AngularJS and NodeJS). This is the last part of three-post series tutorial

MEAN Stack tutorial series:

  1. AngularJS tutorial for beginners (Part I)
  2. Creating RESTful APIs with NodeJS and MongoDB Tutorial (Part II)
  3. MEAN Stack Tutorial: MongoDB, ExpressJS, AngularJS and NodeJS (Part III) 👈 you are here

Before completing the app, let’s cover some background about the this stack. If you rather jump to the hands-on part click here to get started.

1. Why MEAN stack?

TL; DR: NodeJS has been built from bottom up a non-blocking I/O paradigm, which gives you more efficiency per CPU core than using threads in other languages like Java.

LAMP (Linux-Apache-MySQL-PHP) has dominated web application stack for many years now. Well-known platforms such as Wikipedia, Wordpress, and even Facebook uses it or started with it. Enterprise, usually, used go down the Java path: Hibernate, Spring, Struts, JBoss. More agile frameworks also have been used such as Ruby on Rails and for Python Django and Pylon.

Ubiquitous

Well, it turns out, that JavaScript it is everywhere. It used to be limited to browsers. But, now you can found it in smartphones, servers, robots, Arduino, RaspberryPi… Thus, it does not matter what technology you use to build web applications, you need to be familiar with Javascript. In that case, then, it is a time saver to use wherever it fits, especially for building web applications. MEAN stack is embracing that, using Javascript to create end-to-end web applications. ​ Non-blocking architecture

JavaScript is a dynamic, object-oriented, and functional scripting language. One of the features that make it win over Java Applets decades ago, it was its lightness and non-blocking event loop. Blocking means that when one line of code is executing, the rest of it is locked waiting to finish. On the other hand, non-blocking gives to each line of code a shot and then through callbacks it can come back when an event happens. Programming languages that are blocking (Java, Ruby, Python, PHP, …) overcomes concurrency using many threads of execution while JavaScript handles it using non-blocking event loop in a single thread.

As you can see, a single thread of execution in Node can handle perform multiple tasks vs a non-blocking style that execute each one sequentially. You can read more about it in NodeJS faster than Java article.

Some companies like Paypal moved from Java backend to NodeJS and reported a increased performance, lower average response times, and development speed gains. Similarly happens to Groupon that came from Java/Rails monoliths.

Agile and vibrant community

The community behind Javascript is quite vibrant. It has permeated in almost all the fields of technology: data visualization, server-side, databases, robotics, building tools and many more.

2. TODO app with MEAN

In this section are going to put together everything that we learnt in the two previous tutorials.

2.1 MEAN Backend with MongoDB, ExpressJS and NodeJS

In the previous post, we have gone through the process of building a RESTful API and we are going to be building on top of that. Repository here.

git clone https://github.com/amejiarosario/todoAPIjs.git

2.2 MEAN stack front-end with AngularJS

Similarly, we have build a very lean todoApp in the first part of this tutorial. You can download the file to follow along and see it in action here. You might notice the angularJS app is very simple and even it is entirely in one file for simplicity sake. In further tutorials, we are going to make it more modular, split in files, add tests and stylesheets.

Let’s go first to the ExpressJS app (todoAPIjs) and review the default routing system:

  1. AngularJS tutorial for beginners (Part I)
  2. Creating RESTful APIs with NodeJS and MongoDB Tutorial (Part II)
  3. MEAN Stack Tutorial: MongoDB, ExpressJS, AngularJS and NodeJS (Part III) 👈 you are here
// app.js
var routes = require('./routes/index');
app.use('/', routes);

// ./routes/index.js
router.get('/', function(req, res) {
  res.render('index', { title: 'Express' });
});

// ./views/index.ejs
    <h1><%= title %></h1>
    <p>Welcome to <%= title %></p>

The best place to load our ./views/index.ejs. So let’s copy the body content from ngTodo.html content in there and change in ./routes/index.js title to “ngTodo App”. Don’t forget to add ng-app on the top. <html ng-app="app">.

diff

3. Wiring up the App 3.1 AngularJS Read with $http

As you might notice, in the factory, we have a fixed array. We need to change it to communicate with the API that we just build.

$http is Anguar core sevice that allow to make XMLHttpRequest or jsonp request. You can either pass an object with http verb and url or call call $http.verb ($http.get, $http.post).

$http returns a promise which has a success and error function.

$http({method: 'GET', url: '/todos'}).
  success(function(data, status, headers, config) {
    // this callback will be called asynchronously
    // when the response is available.
    console.log('todos: ', data );
  }).
  error(function(data, status, headers, config) {
    // called asynchronously if an error occurs
    // or server returns response with an error status.
    console.log('Oops and error', data);
  });

Let’s try it out in our app. Go to views/index.ejs and do this changes:

// Service
.factory('Todos', ['$http', function($http){
  return $http.get('/todos');
}])

// Controller
.controller('TodoController', ['$scope', 'Todos', function ($scope, Todos) {
  Todos.success(function(data){
    $scope.todos = data;
  }).error(function(data, status){
    console.log(data, status);
    $scope.todos = [];
  });
}])

diff

$http.get will request data using the GET method.

Try it in your browser!s If you have data from the previous tutorial you should be able to see it.
To start the server, you can use

npm start

or if you have it installed

nodemon

3.2 AngularJS Read with $resource

If you click in one of the Todo elements and get redirected to the detail page, you will not see anything yet. We need to update the TodoDetailCtrl first. Even though we already have the GET verb working. We have a different URL requirement for /todos/:id for the other methods. There’s an Angular service that has a higher level of abstraction of $http to deal with RESTful requests. It is called $resource.

Initialize as: $resource(url, [paramDefaults], [actions], options);

It comes with the following actions already defined; it is missing one though… Can you tell?

{ 'get':    {method:'GET'},  // get individual record
  'save':   {method:'POST'}, // create record
  'query':  {method:'GET', isArray:true}, // get list all records
  'remove': {method:'DELETE'}, // remove record
  'delete': {method:'DELETE'} }; // same, remove record

The instances are used in the following way (examples will come later):

  • GET: Resource.get([parameters], [success], [error])
  • Non-GET: Resource.action([parameters], postData, [success], [error])
  • Non-GET: resourceInstance.$action([parameters], [success], [error])

$resource is not part of the Angular core, so it requires to ngResource and the dependency. We can get it from the CDN:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular-resource.min.js"></script>

This is what need to set it up:

  // add ngResource dependency
  angular.module('app', ['ngRoute', 'ngResource'])

  // ...

        .factory('Todos', ['$resource', function($resource){
          return $resource('/todos/:id', null, {
            'update': { method:'PUT' }
          });
        }])
// ...
        .controller('TodoController', ['$scope', 'Todos', function ($scope, Todos) {
          $scope.todos = Todos.query();
        }])

Angular will render an empty $scope.todos. but, when Todos.query() comes with the data from the server it will re-render the UI.

diff

3.3 AngularJS Create

We will need to create a new text box, a button to send a POST request to server and add it to the $scope.

Try it in your browser!s If you have data from the previous tutorial you should be able to see it.
Add this code at the bottom of the id="/todos.html" template:

New task <input type="text" ng-model="newTodo"><button ng-click="save()">Create</button>

Notice that we are using a new directive ng-click, this one executes a function when it clicked. Angular makes sure that the behaviour is consistent across different browsers.

.controller('TodoController', ['$scope', 'Todos', function ($scope, Todos) {
  $scope.todos = Todos.query();

  $scope.save = function(){
    if(!$scope.newTodo || $scope.newTodo.length < 1) return;
    var todo = new Todos({ name: $scope.newTodo, completed: false });

    todo.$save(function(){
      $scope.todos.push(todo);
      $scope.newTodo = ''; // clear textbox
    });
  }
}])

diff

3.4 Show Todo details

Every time you click a todo link, it is showing an empty fields. Let’s fix that. First we need set the real _id to the links instead of $index.

<li ng-repeat="todo in todos | filter: search">
  <input type="checkbox" ng-model="todo.completed">
  <a href="#/{{todo._id}}">{{todo.name}}</a>
</li>
.controller('TodoDetailCtrl', ['$scope', '$routeParams', 'Todos', function ($scope, $routeParams, Todos) {
  $scope.todo = Todos.get({id: $routeParams.id });
}])

Notice the change from $scope.todo = Todos[$routeParams.id]; to $scope.todo = Todos.get({id: $routeParams.id });

Now you should be able to see the details :)

diff

3.5 AngularJS Update (in-line editing)

This is going to be a very cool feature. Let’s meet these new directives:

  • GET: Resource.get([parameters], [success], [error])
  • Non-GET: Resource.action([parameters], postData, [success], [error])
  • Non-GET: resourceInstance.$action([parameters], [success], [error])

Replace the template with id="/todos.html" with the following:

<!-- Template -->
<script type="text/ng-template" id="/todos.html">
  Search: <input type="text" ng-model="search.name">
  <ul>
    <li ng-repeat="todo in todos | filter: search">
      <input type="checkbox" ng-model="todo.completed" ng-change="update($index)">
      <a ng-show="!editing[$index]" href="#/{{todo._id}}">{{todo.name}}</a>
      <button ng-show="!editing[$index]" ng-click="edit($index)">edit</button>

      <input ng-show="editing[$index]" type="text" ng-model="todo.name">
      <button ng-show="editing[$index]" ng-click="update($index)">Update</button>
      <button ng-show="editing[$index]" ng-click="cancel($index)">Cancel</button>
    </li>
  </ul>
  New task <input type="text" ng-model="newTodo"><button ng-click="save()">Create</button>
</script>

Now let’s change the controller to handle the inline editing:

.controller('TodoController', ['$scope', 'Todos', function ($scope, Todos) {
  $scope.editing = [];
  $scope.todos = Todos.query();

  $scope.save = function(){
    if(!$scope.newTodo || $scope.newTodo.length < 1) return;
    var todo = new Todos({ name: $scope.newTodo, completed: false });

    todo.$save(function(){
      $scope.todos.push(todo);
      $scope.newTodo = ''; // clear textbox
    });
  }

  $scope.update = function(index){
    var todo = $scope.todos[index];
    Todos.update({id: todo._id}, todo);
    $scope.editing[index] = false;
  }

  $scope.edit = function(index){
    $scope.editing[index] = angular.copy($scope.todos[index]);
  }

  $scope.cancel = function(index){
    $scope.todos[index] = angular.copy($scope.editing[index]);
    $scope.editing[index] = false;
  }
}])

We added a new variable $scope.editing which shows or hides the form to edit the values. Furthermore, notice ng-click functions: edit, update and cancel.

Try it in your browser!s If you have data from the previous tutorial you should be able to see it.
While were are editing notice that we copy the original todo task into the editing variable. This server for two purposes:

  1. AngularJS tutorial for beginners (Part I)
  2. Creating RESTful APIs with NodeJS and MongoDB Tutorial (Part II)
  3. MEAN Stack Tutorial: MongoDB, ExpressJS, AngularJS and NodeJS (Part III) 👈 you are here

Now, going to the Todo Details. We would like that to be updated as well and add notes.

<script type="text/ng-template" id="/todoDetails.html">
  <h1>{{ todo.name }}</h1>
  completed: <input type="checkbox" ng-model="todo.completed"><br>
  note: <textarea ng-model="todo.note"></textarea><br><br>

  <button ng-click="update()">Update</button>
  <a href="/">Cancel</a>
</script>

Similarly, we added an update method. However, this time we do not need to pass any index, since it is just one todo at a time. After it has been saved, it goes back to root path /.

.controller('TodoDetailCtrl', ['$scope', '$routeParams', 'Todos', '$location', function ($scope, $routeParams, Todos, $location) {
  $scope.todo = Todos.get({id: $routeParams.id });

  $scope.update = function(){
    Todos.update({id: $scope.todo._id}, $scope.todo, function(){
      $location.url('/');
    });
  }
}])

Try it in your browser!s If you have data from the previous tutorial you should be able to see it.
$location.url([url]) is a getter/setter method that allows us to change url, thus routing/view.

diff

3.6 AngularJS Delete

These are the changes added to perform the remove functionality:

A. Add removes button in the li element:

<button ng-show="!editing[$index]" ng-click="remove($index)">remove</button>

Do the same for the details Template

<button ng-click="remove()">Remove</button>

B. Add remove functionality in the controllers

$scope.remove = function(index){
  var todo = $scope.todos[index];
  Todos.remove({id: todo._id}, function(){
    $scope.todos.splice(index, 1);
  });
}

And also in the details controllers

$scope.remove = function(){
  Todos.remove({id: $scope.todo._id}, function(){
    $location.url('/');
  });
}

When we remove elements from the todos array $scope.todos.splice(index, 1) they also disappear from the DOM. Very cool, huh?

diff

Try it in your browser!s If you have data from the previous tutorial you should be able to see it.

*Originally published at *adrianmejia.com

=================

Thanks for reading ❤

If you liked this post, share it with all of your programming buddies!

Follow me on Facebook | Twitter

Learn More

☞ The Complete Node.js Developer Course (3rd Edition)

☞ Angular & NodeJS - The MEAN Stack Guide

☞ MERN Stack Front To Back: Full Stack React, Redux & Node.js

☞ Node, Express, Angular 7, GraphQL and MongoDB CRUD Web App

☞ Angular 7 (formerly Angular 2) - The Complete Guide

☞ MongoDB - The Complete Developer’s Guide

☞ What is the MERN stack and how do I use it?

☞ Node.js, ExpressJs, MongoDB and Vue.js (MEVN Stack) Application Tutorial

☞ MEAN Stack Tutorial MongoDB, ExpressJS, AngularJS and NodeJS

☞ Full Stack Developers: Everything You Need to Know

Building a simple app using NodeJS, MongoDB, and ExpressJS

Building a simple app using NodeJS, MongoDB, and ExpressJS

In this tutorial, we will build a simple app with Node.Js using ExpressJs framework and MongoDB.

In this tutorial, we will build a simple app with Node.Js using ExpressJs framework and MongoDB.

So, first of all, this one is my first blog. I am currently working as a full stack developer but mostly I stuck with front-end today we are going to learn basic operation using Nodejs and MongoDB and also use nodemonI remember when I eventually picked up the courage to try, I had such a hard time understanding the documentation for Express, MongoDB, and Node.js that I gave up.

One month later, I finally understood how to work with these tools. Then, I decided to write a comprehensive blog so you won’t have to go through the same headache I went through.

one thing you need to install node …. I know you can do this…. nodejs

To check if you have Node installed, open up your command line and run the following code: $node -v You should get a version number if you have Node installed. If you don’t, you can install Node either

Start by creating a folder for this project. Feel free to call it anything you want. Once you navigate into it, run the npm init command. This command creates a package.json file which helps you manage dependencies that we install later in the blog.

The simplest way to use node is to run the node command and specify a path to a file. Let’s create a file called server.js to run node with

$ touch server.js

When the execute the server.js file, we want to make sure it’s running properly. To do so, simply write a console.log statement in server.js

console.log('pdp pdp pdp pdp')

Now, run node server.js in your command line and you should see the statement you logged — — pdp pdp pdp pdp

We first have to install Express before we can use it in our application. Installing Express is pretty easy. All we have to do is run an install command with Node package manager (npm), which comes bundled with Node. Run the npm install express --save command in your command line

$ npm install express --save

Once you’re done, you should see that npm has saved Express as a dependency in package.json

Next, we use express in server.js by requiring it.

const express = require('express');
const app = express();

The first thing we want to do is to create a server where browsers can connect. We can do so with the help of a listen method provided by Express:

app.listen(3000, function() {
  console.log('listening on 3000')
})

Now, run node server.js and navigate to localhost:3000 on your browser. You should see a message that says “cannot get /”. nice great news just kidding….. this is just beginning. The READ operation is performed by browsers whenever you visit a webpage. Under the hood, browsers send a GET request to the server to perform a READ operation. The reason we see the “cannot get /” error is because we have yet to send anything back to the browser from our server.

In Expressjs, we handle a GET request with the get method:

app.get(path, callback)

The first argument, path, is the path of the GET request. It’s anything that comes after your domain name.

When we’re visiting localhost:3000, our browsers are actually looking for localhost:3000/. The path argument in this case is /.The second argument is a callback function that tells the server what to do when the path is matched. It takes two arguments, a request object and a response object:

app.get('/', function (request, response) {
   // work here
})

For now, let’s write “PDP” back to the browser. We do so by using a sendmethod that comes with the response object:

app.get('/', function(req, res) {
   res.send('PDP')
})

I’m going to start writing in ES6 code and show you how to convert to ES6 along the way as well. First off, I’m replacing function() with an ES6 arrow function. The below code is the same as the above code:

app.get('/', (req, res) => {
   res.send('PDP') 
})

Now, restart your server by doing the following:

  1. Stop the current server by hitting CTRL + C in the command line.
  2. Run node server.js again.

Then, navigate to localhost:3000 on your browser. You should be able to see a string that says “PDP”.

Great. Let’s change our app so we serve an index.html page back to the browser instead. To do so, we use the sendFile method that’s provided by the res object.

res.sendFile(__dirname + '/index.html')

In the sendFile method above, we told Express to serve an index.html file that can be found in the root of your project folder. We don’t have that file yet. Let’s make it now.

touch index.html

Let’s put some text in our index.html file as well:


 
 
   
   MY APP
 
 
   May Node and Express be with you.
 
 

Restart your server and refresh your browser. You should be able to see the results of your HTML file now.

This is how Express handles a GET request (READ operation) in a nutshell.

At this point, you probably have realized that you need to restart your server whenever you make a change to server.js. This is process is incredibly tedious, so let’s take a quick detour and streamline it by using a package called nodemon.

Nodemon restarts the server automatically whenever you save a file that the server uses. We can install Nodemon by using the following command:

$ npm install nodemon --save-dev

Note: The reason we’re using a --save-dev flag here is because we’re only using Nodemon when we’re developing. This flag would save Nodemon as a devDependency in your package.json file.

Moving on, Nodemon behaves exactly the same as node, which means we can run our server by calling nodemon server.js. However, we can’t do it in the command line right now because Nodemon isn’t installed with a -g flag.

There’s one other way to run Nodemon — we can execute Nodemon from the node_modules folder. The code looks like this:

$ ./node_modules/.bin/nodemon server.js

That’s a handful to type. One way to make it simpler is to create a script key in package.json.

"scripts": {
  "dev": "nodemon server.js"
}

Now, you can run npm run dev to trigger nodemon server.js.

Back to the main topic. We’re going to cover the CREATE operation next.

The CREATE operation is only performed by the browser if a POSTrequest is sent to the server. This POST request can be triggered either with JavaScript or through a `` element.

Let’s find out how to use a `` element to create new entries for our Star Wars quote app for this part of the tutorial.

To do so, you first have to create a `` element and add it to your index.html file. You need to have three things on this form element:

  1. An action attribute,
  2. method attribute,
  3. and name attributes on all ` elements within the form.

   
   
   Submit
 

The action attribute tells the browser where to navigate to in our Express app. In this case, we’re navigating to /quotes. The methodattribute tells the browser what request to send. In this case, it’s a POST request.

On our server, we can handle this POST request with a post method that Express provides. It takes the same arguments as the GET method:

app.post('/quotes', (req, res) => { 
  console.log('Hellooooooooooooooooo!')
})

Restart your server (hopefully you’ve set up Nodemon so it restarts automatically) and refresh your browser. Then, enter something into your form element. You should be able to see Hellooooooooooooooooo!in your command line.

Great, we know that Express is handling the form for us right now. The next question is, how do we get the input values with Express?

Turns out, Express doesn’t handle reading data from the ``element on it’s own. We have to add another package called body-parserto gain this functionality.

$ npm install body-parser --save

Express allows us to add middleware like body-parser to our application with the use method. You’ll hear the term middleware a lot when dealing with Express. These things are basically plugins that change the request or response object before they get handled by our application. Make sure you place body-parser before your CRUD handlers!

const express = require('express')
const bodyParser= require('body-parser') 
const app = express()  
app.use(bodyParser.urlencoded({extended: true}))

The urlencoded method within body-parser tells body-parser to extract data from the `` element and add them to the body property in the requestobject.

Now, you should be able to see everything in the form field within the req.body object. Try doing a console.log and see what it is!

app.post('/quotes', (req, res) => {
 console.log(req.body) 
})
You should be able to get an object similar to the following in your command line:
{
name: 'pdp',
quotes: 'pdp psr pdp'
}

Enter the Database, MongoDB

We first have to install MongoDB through npm if we want to use it as our database.

$ npm install mongodb --save

Once installed, we can connect to MongoDB through the MongoClient's connect method as shown in the code below:

const MongoClient = require('mongodb').MongoClient  MongoClient.connect('link-to-mongodb', (err, database) => {
   // ... start the server 
})

The next part is to get the correct link to our database. Most people store their databases on cloud services like mLab (formerly MongoLab). We’re going to do same as well.

So, go ahead and create an account with mLab. (It’s free.) Once you’re done, create a new MongoDB Deployment

Once you’re done creating the deployment, head into it and create a database user and database password. Remember the database user and database password because you’re going to use it to connect the database you’ve just created.

Finally, grab the MongoDB url and add it to your MongoClient.connectmethod. Make sure you use your database user and password!

Next, we want to start our servers only when the database is connected. Hence, let’s move app.listen into the connect method. We’re also going to create a db variable to allow us to use the database when we handle requests from the browser.

const client = new MongoClient(url, { useNewUrlParser: true });
client.connect((err, database) => {
 db = database.db("test")
 app.listen(3000, function () {
 })
})

We’re done setting up MongoDB. Now, let’s create a quotes collection to store quotes for our application.

By the way, a collection is a named location to store stuff. You can create as many collections as you want. It can be things like “products”, “quotes”, “groceries”, or any other labels you choose.

We can create the quotes collection by using the string quotes while calling MongoDB’s db.collection() method. While creating the quotes collection, we can also save our first entry into MongoDB with the savemethod simultaneously.

Once we’re done saving, we have to redirect the user somewhere (or they’ll be stuck waiting forever for our server to move). In this case, we’re going to redirect them back to /, which causes their browsers to

app.post('/quotes', (req, res) => {   db.collection('quotes').save(req.body, (err, result) => {
     if (err) return console.log(err)
     console.log('saved to database')
     res.redirect('/')
   })
 })

Now, if you enter something into the `` element, you’ll be able to see an entry in your MongoDB collection.

oh, sorry button is also there maybe I made mistake but leave it now…

Whoohoo! Since we already have some quotes in the collection, why not try showing them to our user when they land on our page?

We have to do two things to show the quotes stored in mLab to our users.

  1. Get quotes from mLab.
  2. Use a template engine to display the quotes.

Let’s go one step at a time.

We can get the quotes from mLab by using the find method that’s available in the collection method.

app.get('/', (req, res) => {
   var cursor = db.collection('quotes').find() 
})

The find method returns a cursor (a Mongo object) that probably doesn’t make sense if you console.log it out. this cursor object contains all quotes from our database. It also contains a bunch of other properties and methods that allow us to work with data easily. One such method is the toArraymethod.The toArray method takes in a callback function that allows us to do stuff with quotes we retrieved from mLab. Let’s try doing a console.log() for the results and see what we get!

var cursor = db.collection('quotes').find().toArray(function(err, result){
    console.log('kya baat bhai mast')
    if (err) { return console.log(err) }
    console.log('haanbhai', result)
    res.render('index.ejs', {quotes: result})
  })

We can’t serve our index.html file and expect quotes to magically appear because there’s no way to add dynamic content to a HTML file. What we can do instead, is to use template engines to help us out. Some popular template engines include Jade, Embedded JavaScript and Nunjucks.

I’ve written extensively about the how and why of template engines in a separate post. You might want to check it out if you have no idea what template engines are. I personally use (and recommend) Nunjucks as my template engine of choice. Feel free to check out the post to find out why.

For this tutorial, we’re going to use Embedded JavaScript (ejs) as our template engine because it’s the easiest to start with. You’ll find it familiar from the get-go since you already know HTML and JavaScript.

We can use EJS by first installing it, then setting the view engine in Express to ejs.

$ npm install ejs --save
code in server.js
app.set('view engine', 'ejs')

Once the view engine is set, we can begin generating the HTML with our quotes. This process is also called rendering. We can use the render object built into the response object render to do so. It has the following syntax:

res.render(view, locals)

The first parameter, view, is the name of the file we’re rendering. This file must be placed within a views folder.

The second parameter, locals, is an object that passes data into the view.

Let’s first create an index.ejs file within the views folder so we can start populating data.

$ mkdir views
$ touch views/index.ejs

Now, place the following code within index.ejs.


   
     
       
       
     
   
 

See what I mean when I say you’ll find it familiar? In EJS, you can write JavaScript within  tags. You can also output JavaScript as strings if you use the  tags.

Here, you can see that we’re basically looping through the quotes array and creating strings with quotes[i].name and quotes[i].quote.

One more thing to do before we move on from the index.ejs file. Remember to copy the `` element from the index.html file into this file as well. The complete index.ejs file so far should be:




  
  MY APP


  May Node and Express be with you. and pdp ki blessing


  
    
      
      
    
  
  


    
    
    Submit
  


Finally, we have to render this index.ejs file when handling the GETrequest. Here, we’re setting the results (an array) as the quotes array we used in index.ejs above.

app.get('/', (req, res) => {
  // res.sendFile(__dirname + '/index.html');
  var cursor = db.collection('quotes').find().toArray(function(err, result){
    console.log('kya baat bhai mast')
    if (err) { return console.log(err) }
    console.log('haanbhai', result)
    res.render('index.ejs', {quotes: result})
  })
})

Now, refresh your browser and you should be able to see quotes.

AngularJS tutorial for beginners with NodeJS, ExpressJS and MongoDB

AngularJS tutorial for beginners with NodeJS, ExpressJS and MongoDB

This tutorial is meant to be as clear as possible. At the same time, we are going to cover the concepts that you will need most of the time. All the good stuff without the fat :)

This tutorial is meant to be as clear as possible. At the same time, we are going to cover the concepts that you will need most of the time. All the good stuff without the fat :)

MEAN Stack tutorial series:

  1. AngularJS tutorial for beginners (Part I) 👈 you are here
  2. Creating RESTful APIs with NodeJS and MongoDB Tutorial (Part II)
  3. MEAN Stack Tutorial: MongoDB, ExpressJS, AngularJS and NodeJS (Part III)

We are going to start building all the examples in a single HTML file! It embedded JavaScript and NO styles/CSS for simplicity. Don’t worry, in the next tutorials, we will learn how to split use Angular modules. We are going to break down the code, add testing to it and styles.

What is Angular.js?

Angular.js is a MVW (Model-View-Whatever) open-source JavaScript web framework that facilitates the creation of single-page applications (SPA) and data-driven apps.

AngularJS vs jQuery vs BackboneJS vs EmberJS

TL; DR: AngularJS is awesome for building testable single page applications (SPA). Also, excel with data-driven and CRUD apps. Show me the code!.

AngularJS motto is

HTML enhanced for web apps!
It extends standard HTML tags and properties to bind events and data into it using JavaScript. It has a different approach to other libraries. jQuery, Backbone.Js, Ember.js and similar… they are more leaned towards “Unobtrusive JavaScript”.

Traditional JavaScript frameworks, use IDs and classes in the elements. That gives the advantage of separating structure (HTML) from behavior (Javascript). Yet, it does not do any better on code complexity and readability. Angular instead declares the event handlers right in the element that they act upon.

Times have changed since then. Let’s examine how AngularJS tries to ease code complexity and readability:

  • Unit testing ready: JavaScript is, usually, hard to unit test. When you have DOM manipulations and business logic together (e.g. jQuery based code). AngularJS keeps DOM manipulation in the HTML and business logic separated. Data and dependencies are $injected as needed.
  • DOM manipulation where they are used. It decouples DOM manipulation from application logic.
  • AngularJS is also excellent for single-page applications (SPA).
  • Different browsers implement features differently, but fret not. Angular’s directive (or HTML extensions) take care of the differences for you.
  • Global namespace expressions and methods definitions are scoped within controllers. So, they do not pollute the global namespace.
  • Data models are plain old JavaScript objects (POJO).
  • Write less code: AngualarJS features save you from much boilerplate code.
  • AngularJS provides solutions for writing modular code and dependencies management.

Without further ado, let’s dive in!

2. AngularJS Main Components

AngularJS has an extensive API and components. In this tutorial we are going to focus on the most important ones, such as directives, modules, services, controllers and related concepts.

2.1 AngularJS Directives

The first concept you need to know about AngularJS is what are directives.

Directives are extensions of HTML markups. They could take the form of attributes, element names, CSS class and or even HTML comments. When the AngularJS framework is loaded, everything inside ng-app it’s compiled. The directives are bound to data, events, and DOM transformations.

Notice in the following example that there are two directives: ng-app and ng-model.

Notice in the following example that there are two directives: ng-app and ng-model.

<html ng-app>
<head>
  <title>Hello World in AngularJS</title>
</head>
<body>

<input ng-model="name"> Hello {{ name }}

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular.min.js"></script>
</body>
</html>

We going to learn about some of the main built-in directives as we go:

  • Unit testing ready: JavaScript is, usually, hard to unit test. When you have DOM manipulations and business logic together (e.g. jQuery based code). AngularJS keeps DOM manipulation in the HTML and business logic separated. Data and dependencies are $injected as needed.
  • DOM manipulation where they are used. It decouples DOM manipulation from application logic.
  • AngularJS is also excellent for single-page applications (SPA).
  • Different browsers implement features differently, but fret not. Angular’s directive (or HTML extensions) take care of the differences for you.
  • Global namespace expressions and methods definitions are scoped within controllers. So, they do not pollute the global namespace.
  • Data models are plain old JavaScript objects (POJO).
  • Write less code: AngualarJS features save you from much boilerplate code.
  • AngularJS provides solutions for writing modular code and dependencies management.
2.2 AngularJS Data Binding

Data binding is an AngularJS feature that synchronizes your model data with your HTML. That’s great because models are the “single source of truth”. You do not have to worry about updating them. Here’s a graph from docs.angularjs.org.

Whenever the HTML is changed, the model gets updated. Wherever the model gets updated it is reflected in HTML.

2.3 AngularJS Scope

$scope it is an object that contains all the data to which HTML is bound. They are the glue your javascript code (controllers) and the view (HTML). Everything that is attached to the $scope, it is $watched by AngularJS and updated.

Scopes can be bound to javascript functions. Also, you could have more than one $scope and inherit from outer ones. More on this, in the controller’s section.

2.4 AngularJS Controllers

Angular.js controllers are code that “controls” certain sections containing DOM elements. They encapsulate the behavior, callbacks and glue $scope models with views. Let’s see an example to drive the concept home:

<body ng-controller="TodoController">
  <ul>
    <li ng-repeat="todo in todos">
      <input type="checkbox" ng-model="todo.completed">
      {% raw  %}{{ todo.name }}{% endraw %}
    </li>
  </ul>

  <script>
    function TodoController($scope){
      $scope.todos = [
        { name: 'Master HTML/CSS/Javascript', completed: true },
        { name: 'Learn AngularJS', completed: false },
        { name: 'Build NodeJS backend', completed: false },
        { name: 'Get started with ExpressJS', completed: false },
        { name: 'Setup MongoDB database', completed: false },
        { name: 'Be awesome!', completed: false },
      ]
    }
  </script>
</body>

As you might notice we have new friends: ng-controller, ng-repeat and $scope.

  • Unit testing ready: JavaScript is, usually, hard to unit test. When you have DOM manipulations and business logic together (e.g. jQuery based code). AngularJS keeps DOM manipulation in the HTML and business logic separated. Data and dependencies are $injected as needed.
  • DOM manipulation where they are used. It decouples DOM manipulation from application logic.
  • AngularJS is also excellent for single-page applications (SPA).
  • Different browsers implement features differently, but fret not. Angular’s directive (or HTML extensions) take care of the differences for you.
  • Global namespace expressions and methods definitions are scoped within controllers. So, they do not pollute the global namespace.
  • Data models are plain old JavaScript objects (POJO).
  • Write less code: AngualarJS features save you from much boilerplate code.
  • AngularJS provides solutions for writing modular code and dependencies management.
2.5 AngularJS Modules

Modules are a way to encapsulate different parts of your application. They allow reusing code in other places. Here’s an example of how to rewrite our controller using modules.

angular.module('app', [])
  .controller('TodoController', ['$scope', function ($scope) {
    $scope.todos = [
      { title: 'Learn Javascript', completed: true },
      { title: 'Learn Angular.js', completed: false },
      { title: 'Love this tutorial', completed: true },
      { title: 'Learn Javascript design patterns', completed: false },
      { title: 'Build Node.js backend', completed: false },
    ];
  }]);

Using modules brings many advantages. They can be loaded in any order, and parallel dependency loading. Also, tests can only load the required modules and keep it fast, clear view of the dependencies.

2.6 AngularJS Templates

Templates contain HTML and Angular elements (directives, markup, filters or form controls). They can be cached and referenced by an id.

Here’s an example:

<script type="text/ng-template" id="/todos.html">
  <ul>
    <li ng-repeat="todo in todos">
      <input type="checkbox" ng-model="todo.completed">
      {{ todo.name }}
    </li>
  </ul>
</script>

Does the code inside looks familiar? ;)

Notice they are inside the script and has a type of text/ng-template.

2.7 AngularJS Routes (ngRoutes)

ngRoutes module allows changing what we see in the app depending on the URL (route). It, usually, uses templates to inject the HTML into the app.

It does not come with AngularJS core module, so we have to list it as a dependency. We are going to get it from Google CDN:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular-route.min.js"></script>

NEW FEATURE: add notes to the todo tasks. Let’s start with the routes!

angular.module('app', ['ngRoute'])
  .config(['$routeProvider', function ($routeProvider) {
    $routeProvider
      .when('/', {
        templateUrl: '/todos.html',
        controller: 'TodoController'
      });
  }]);

  • Unit testing ready: JavaScript is, usually, hard to unit test. When you have DOM manipulations and business logic together (e.g. jQuery based code). AngularJS keeps DOM manipulation in the HTML and business logic separated. Data and dependencies are $injected as needed.
  • DOM manipulation where they are used. It decouples DOM manipulation from application logic.
  • AngularJS is also excellent for single-page applications (SPA).
  • Different browsers implement features differently, but fret not. Angular’s directive (or HTML extensions) take care of the differences for you.
  • Global namespace expressions and methods definitions are scoped within controllers. So, they do not pollute the global namespace.
  • Data models are plain old JavaScript objects (POJO).
  • Write less code: AngualarJS features save you from much boilerplate code.
  • AngularJS provides solutions for writing modular code and dependencies management.
2.8 AngularJS Services (Factories)

Notice that if you want to create a 2nd controller and share $scope.todos it is not possible right now. That is when services become handy. Services are a way to inject data dependencies into controllers. They are created through factories. Let’s see it in action:

angular.module('app', ['ngRoute'])

  .factory('Todos', function(){
    return [
      { name: 'AngularJS Directives', completed: true },
      { name: 'Data binding', completed: true },
      { name: '$scope', completed: true },
      { name: 'Controllers and Modules', completed: true },
      { name: 'Templates and routes', completed: true },
      { name: 'Filters and Services', completed: false },
      { name: 'Get started with Node/ExpressJS', completed: false },
      { name: 'Setup MongoDB database', completed: false },
      { name: 'Be awesome!', completed: false },
    ];
  })

  .controller('TodoController', ['$scope', 'Todos', function ($scope, Todos) {
    $scope.todos = Todos;
  }])

We are now injecting the data dependency Todo into the controllers. This way we could reuse the data to any controller or module that we need to. This is not only used for static data like the array. But we could also do server calls using $http or even RESTful $resource.

This is what is happening:

  1. AngularJS tutorial for beginners (Part I) 👈 you are here
  2. Creating RESTful APIs with NodeJS and MongoDB Tutorial (Part II)
  3. MEAN Stack Tutorial: MongoDB, ExpressJS, AngularJS and NodeJS (Part III)

NOTE: in codepen, you will not see the URL. If you want to see it changing, you can download the whole example an open it from here.

2.9 AngularJS Filters

Filters allow you to format and transform data. They change the output of expressions inside the curly braces. AngularJS comes with a bunch of useful filters.

Built-in Filters:

  • Unit testing ready: JavaScript is, usually, hard to unit test. When you have DOM manipulations and business logic together (e.g. jQuery based code). AngularJS keeps DOM manipulation in the HTML and business logic separated. Data and dependencies are $injected as needed.
  • DOM manipulation where they are used. It decouples DOM manipulation from application logic.
  • AngularJS is also excellent for single-page applications (SPA).
  • Different browsers implement features differently, but fret not. Angular’s directive (or HTML extensions) take care of the differences for you.
  • Global namespace expressions and methods definitions are scoped within controllers. So, they do not pollute the global namespace.
  • Data models are plain old JavaScript objects (POJO).
  • Write less code: AngualarJS features save you from much boilerplate code.
  • AngularJS provides solutions for writing modular code and dependencies management.

Note you can also chain many filters and also define your own filters.

HTML enhanced for web apps!

<script type="text/ng-template" id="/todos.html">
  Search: <input type="text" ng-model="search.name">
  <ul>
    <li ng-repeat="todo in todos | filter: search">
      <input type="checkbox" ng-model="todo.completed">
      <a href="#/{{$index}}">{{todo.name}}</a>
    </li>
  </ul>
</script>

Notice that we are using search.name in the ng-model for search. That will limit the search to the name attribute and search.notes will look inside the notes only. Guest what search would do then? Precisely! It searches in all the attributes. Fork the following example and try it out:

ng-test

Congrats, you have reached this far! It is time to test what you have learned. Test-Driven Learning (TDL) ;). Here’s the challenge: open this file on your favorite code editor. Copy the boilerplate code and built the full app that we just build in the previous examples. Of course, you can take a peek from time to time if you get stuck ;)

Download this file as…:

index.html

ng-solution

This is the full solution and you can see it live in here.

<html ng-app="app">
<head>
  <title>ngTodo</title>
</head>
<body>

<ng-view></ng-view>

<!-- Libraries -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular-route.min.js"></script>

<!-- Template -->
<script type="text/ng-template" id="/todos.html">
  Search: <input type="text" ng-model="search.name">
  <ul>
    <li ng-repeat="todo in todos | filter: search">
      <input type="checkbox" ng-model="todo.completed">
      <a href="#/{{$index}}">{{todo.name}}</a>
    </li>
  </ul>
</script>

<script type="text/ng-template" id="/todoDetails.html">
  <h1>{{ todo.name }}</h1>
  completed: <input type="checkbox" ng-model="todo.completed">
  note: <textarea>{{ todo.note }}</textarea>
</script>

<script>
  angular.module('app', ['ngRoute'])
    //---------------
    // Services
    //---------------
    .factory('Todos', function(){
      return [
        { name: 'AngularJS Directives', completed: true, note: 'add notes...' },
        { name: 'Data binding', completed: true, note: 'add notes...' },
        { name: '$scope', completed: true, note: 'add notes...' },
        { name: 'Controllers and Modules', completed: true, note: 'add notes...' },
        { name: 'Templates and routes', completed: true, note: 'add notes...' },
        { name: 'Filters and Services', completed: false, note: 'add notes...' },
        { name: 'Get started with Node/ExpressJS', completed: false, note: 'add notes...' },
        { name: 'Setup MongoDB database', completed: false, note: 'add notes...' },
        { name: 'Be awesome!', completed: false, note: 'add notes...' },
      ];
    })
    //---------------
    // Controllers
    //---------------
    .controller('TodoController', ['$scope', 'Todos', function ($scope, Todos) {
      $scope.todos = Todos;
    }])
    .controller('TodoDetailCtrl', ['$scope', '$routeParams', 'Todos', function ($scope, $routeParams, Todos) {
      $scope.todo = Todos[$routeParams.id];
    }])
    //---------------
    // Routes
    //---------------
    .config(['$routeProvider', function ($routeProvider) {
      $routeProvider
        .when('/', {
          templateUrl: '/todos.html',
          controller: 'TodoController'
        })
        .when('/:id', {
          templateUrl: '/todoDetails.html',
          controller: 'TodoDetailCtrl'
       });
    }]);
</script>

</body>
</html>

Thanks for reading this far.