AngularJS Based Note Making App In 100 Lines (Core Functionality)

AngularJS is best suited for Single Page Application (SPAs) and this tool suits the purpose of using AngularJS in its best. We are going to develop a custom notepad directive using Angular’s directive. Note - This article assumes the reader to have some experience in developing application using AngularJS. We are going to develop the Note making app using Angular’s factory and directive.
Here’s the use case of our Note Making App.

A user should be able to create, edit and delete notes. Persisting notes should be handled too so the tool can fetch all the existing notes and show the same to user”.

In order to handle the persistence, we are going to make use of browsers local storage. Which is one of the key HTML5 feature. One may ask a question, why the need for AngularJS? How about using a simple JavaScript or JQuery? You are absolutely free to develop using any libraries or framework of your choice. But I would like to also mention the reason why I am going with AngularJS. Like I said in the beginning of the article, the Note Making App is a Single-Page Application and Angular is the best framework for develop such applications. Other than that, I was looking for an interesting real world application that can be developed to learn the AngularJS features like directives, factory. I think it’s really important for a beginner to understand the usage of these features with a real world example through which she/he can get a better understanding and the real usage of AngularJS.

Before diving into the Note Making app implementation, here’s something I would like to share with you. This application is developed or extended based on the existing open source app.

Let us take a look into the snapshot of the Note Making App. Below, you see the note making app running on a browser (Chrome in my case). It is showing two notes for now with a short title of 50 characters. On click of each of the note, you can read full notes and edit the same. There’s a delete button on the right side of each note, you can hit ‘Delete’ button remove the one which you think is no longer needed.

add note

Using Code

Let us now try to understand the inner working of Notes Making App. The following is the code snippet of the index.html, where you can see the script references for JQuery and Angular. You will also notice the usage of <notepad/> which we call as directive in Angular’s world.

  1. <!DOCTYPE html>  
  2. <html ng-app="noteApp">  
  3.   
  4. <head>  
  5.     <meta charset="utf-8" />  
  6.     <title>AngularJS Plunker</title>  
  7.     <script>  
  8. document.write('<base href="' + document.location + '" />');  
  9.     </script>  
  10.     <link rel="stylesheet" href="style.css" />  
  11.     <script data-require="jquery@*" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>  
  12.     <script data-require="angular.js@1.0.x" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js" data-semver="1.0.8"></script>  
  13.     <script src="app.js"></script>  
  14. </head>  
  15.   
  16. <body>  
  17.     <h1 class="title">The Note Making App</h1>  
  18.     <notepad/> </body>  
  19.   
  20. </html>  
Now let us take a look into the behind of the scene of the application. As I said before, we are going to develop a custom directive and create an AngularJS based factory.

Below is the code snippet of ‘notesFactory’ which will be used within the directive that we will be coding next.

Notice the usage of localStorage for managing the user notes. Here’s the snapshot of the local storage items in Google Chrome. We are defining a unique key with the key name as “note” followed by note id which is getting started and incremented by 1.

A note item is saved in localStorage by making a call to setItem method and passing in the key as note<uniqueId> and value as note text entered by the user. The notes are retrieved by making a call to localStorage’s method ‘getItem’ by passing in the keyname.

The “getAll” function returns the list of notes by looping through all the localStorage items. Each of the note item contains a JSON info, one can get the note id, title and content and display the same on UI.

The “getLastNote” function returns the last note info present in the localStorage. By looking at the below code, you may think, why not to return the last note by simply getting the note by localStorage item length. The reason being, say if the user deletes one of the note, there will be a mismatch in the note id vs the localStorage items length and we cannot simply be able to grab the last note item from the localStorage. Note - The code can be definitely improved. For now, let us loop through all items.

The “deleteById” function is used to delete the existing note item in a localStorage. Loop through each of the local storage items and match the localStorage key name, when there’s a match, just remove that item.

deleteById
  1. app.factory('notesFactory', function ()  
  2. {  
  3.     return {  
  4.         put: function (note)  
  5.         {  
  6.             localStorage.setItem('note' + note.id, JSON.stringify(note));  
  7.             return this.getAll();  
  8.         },  
  9.         get: function (index)  
  10.         {  
  11.             return JSON.parse(localStorage.getItem('note' + index));  
  12.         },  
  13.         getLastNote: function ()  
  14.         {  
  15.             var lastNote = [];  
  16.             for (var i = 0; i < localStorage.length; i++)  
  17.             {  
  18.                 if (localStorage.key(i)  
  19.                     .indexOf('note') !== -1)  
  20.                 {  
  21.                     var note = localStorage.getItem(localStorage.key(i));  
  22.                     lastNote = JSON.parse(note);  
  23.                 }  
  24.             }  
  25.             return lastNote;  
  26.         },  
  27.         getAll: function ()  
  28.         {  
  29.             var notes = [];  
  30.             for (var i = 0; i < localStorage.length; i++)  
  31.             {  
  32.                 if (localStorage.key(i)  
  33.                     .indexOf('note') !== -1)  
  34.                 {  
  35.                     var note = localStorage.getItem(localStorage.key(i));  
  36.                     notes.push(JSON.parse(note));  
  37.                 }  
  38.             }  
  39.             return notes;  
  40.         },  
  41.         deleteById: function (index)  
  42.         {  
  43.             for (var i = 0; i < localStorage.length; i++)  
  44.             {  
  45.                 if (localStorage.key(i) == 'note' + index)  
  46.                 {  
  47.                     localStorage.removeItem(localStorage.key(i));  
  48.                     break;  
  49.                 }  
  50.             }  
  51.             return this.getAll();  
  52.         }  
  53.     };  
  54. });  
Now let us create an Angular module and build a “notepad” directive. Below is the code snippet for the same. First, let us create an Angular module and then create start building the directive. Notice below the directive restrict is set with ‘AE’ means the directive can be an element or attribute.

Here’s how you can use the directive as attribute.
  1. <div notepad></div>  
If you are interested in using the below directive as an element. You can follow the below syntax.
  1. <notepad/> or <notepad></notepad>  
The key thing about the directive is scope variables and methods part of the link function. Let us take a step back and thing about the functionality. Our application should handle opening or editor and accept notes, save and edit the existing notes. Also the user should be able to delete the same. So we go ahead and create respective methods based on the supporting functionalities.

Here is what we do within the “openEditor” function: 
  1. Set the editMode to true. Later you will see it’s usage in the HTML view.

  2. The index will be passed in while we are editing the note or it will be undefined. Which means, it’s a new note. If we have a value, get and set the notetext and scope index.

  3. Else you can set the notetext as undefined.

The saving of note text is done as below by making a call to “save” method.

  1. Check whether we have a note text.

  2. Based on the note text, set the note title with a 50 chars limit.

  3. Set the note content, nothing but the note text.

  4. If the index is undefined, which means we are creating a new note text. If so, get the last note and set the note id by incrementing the last note id by one.

  5. Else if you the user is editing an existing note, set the note id with the index.

  6. Make a call to noteFactory put method by passing in the note.

  7. Finally make a call to restore method, so it resets the notetext, editMode and index.

The note deletion is performed by making a call to “delete” scope method. Here’s what we do.

  1. Alter the user with a confirm message about the note being deleted.

  2. If the user clicks “OK” button, make a call to deleteById method of noteFactory by passing in the index to delete.
    1. var app = angular.module('noteApp', []);  
    2. app.directive('notepad', function (notesFactory)  
    3. {  
    4.     return {  
    5.         restrict: 'AE',  
    6.         scope:  
    7.         {},  
    8.         link: function (scope, elem, attrs)  
    9.         {  
    10.             scope.openEditor = function (index)  
    11.             {  
    12.                 scope.editMode = true;  
    13.                 if (index !== undefined)  
    14.                 {  
    15.                     scope.noteText = notesFactory.get(index)  
    16.                         .content;  
    17.                     scope.index = index;  
    18.                 }  
    19.                 else scope.noteText = undefined;  
    20.             };  
    21.             scope.save = function ()  
    22.             {  
    23.                 if (scope.noteText !== "" && scope.noteText !== undefined)  
    24.                 {  
    25.                     var note = {};  
    26.                     note.title = scope.noteText.length > 50 ? scope.noteText.substring(0, 50) + '. . .' : scope.noteText;  
    27.                     note.content = scope.noteText;  
    28.                     if (scope.index == undefined)  
    29.                     {  
    30.                         if (localStorage.length > 0)  
    31.                         {  
    32.                             var existingNote = notesFactory.getLastNote();  
    33.                             note.id = existingNote.id + 1;  
    34.                         }  
    35.                         else  
    36.                         {  
    37.                             note.id = 1;  
    38.                         }  
    39.                     }  
    40.                     else  
    41.                     {  
    42.                         note.id = scope.index;  
    43.                     }  
    44.                     scope.notes = notesFactory.put(note);  
    45.                 }  
    46.                 scope.restore();  
    47.             };  
    48.             scope.delete = function (index)  
    49.             {  
    50.                 var status = confirm('Do you want to Delete?');  
    51.                 if (status) scope.notes = notesFactory.deleteById(index);  
    52.             }  
    53.             scope.restore = function ()  
    54.             {  
    55.                 scope.editMode = false;  
    56.                 scope.index = undefined;  
    57.                 scope.noteText = "";  
    58.             };  
    59.             var editor = elem.find('#editor');  
    60.             scope.restore();  
    61.             scope.notes = notesFactory.getAll();  
    62.             editor.bind('keyup keydown', function ()  
    63.             {  
    64.                 scope.noteText = editor.text()  
    65.                     .trim();  
    66.             });  
    67.         },  
    68.         templateUrl: 'templateurl.html'  
    69.     };  
    70. });  

Here is the snapshot of the Note Making app edit note text. Click on the existing note to edit and then modify the text and save the same.

modify the text

There is one another important thing to discuss. That is about the directive templateUrl. We are setting the directive templateUrl to display the view based on the HTML that we are making use of i.e templateurl.html. Here’s the code snippet for the same.

You can see below, on how the scope methods are being called for adding, saving and deleting a note text. The editMode value hides or shows certain things on the view.

  1. <div class="note-area" ng-show="!editMode">  
  2.     <table>  
  3.         <tr ng-repeat="note in notes|orderBy:'id'">  
  4.             <td width="100%"> <a href="#" ng-click="openEditor(note.id)" class='notetext'>{{note.title}}</a>  
  5.                 <br/> </td>  
  6.             <td>  
  7.                 <button ng-click="delete(note.id)" class='deletebutton'>Delete</button>  
  8.             </td>  
  9.         </tr>  
  10.     </table>  
  11. </div>  
  12. <div id="editor" ng-show="editMode" class="note-area" contenteditable="true" ng-bind="noteText"></div>   
  13. <span>  
  14.     <a href="#" ng-click="save()" ng-show="editMode">Save</a>  
  15. </span>   
  16. <span>  
  17.     <a href="#" ng-click="openEditor()" ng-show="!editMode">Add Note</a>  
  18. </span>  
Running the Application

Let us now see how we can run the application using NodeJS based module http-server. Please make sure to install NodeJS if you don’t have one -
Download NodeJS 

Run the following one command prompt to install the http-server globally.

npm install http-server –g

After installing the http-server, you can simply run the application by specifying the following command:
http-server <path of the application to run>.

path of the application to run

Open up your favorite browser and then navigate to http://localhost:8080/index.html