Angular Js: Introduction

By: Stephen Patrick | 04 Aug 2016 | Category: Angular Js

Angular Js: Introduction

JavaScript allows us to add dynamic behavior to our web applications. It allows us to manipulate, and animate page elements, send and receive data to and from a server, synchronously and asynchronously. Over the last number of years there are many JavaScript frameworks that aim to simplify how we write Web Applications. Some frameworks, abstract the differences between different platforms, provide a programming model for working with the document object model. While, others provide a framework that helps us separate the components of our JavaScript application and the communication between them.

Angular Js, helps us separate the components of our application. It is based on the Model View Controller (MVC) architectural design pattern. There are many different frameworks that adopt this pattern both on the client and on the server. The pattern divides components into areas of responsibility. Model: Data Types that model the data of the domain. View: Represents the UI . *Controller: Manages the interaction between the view and the model.

The general idea is that any change to model should be reflected in the view and vice a versa. We could also have different representations (views) of the same model, and changing the model should update all of its representations.

Lets take a first example of Angular Js. We will create a simple Contact application. In this application we will display a list of contacts and the user can select a contact to edit.

The first file we create will be an html file called index.html. We will add to this file as we introduce angular Js concepts. The file includes the bootstrap style sheets in the header and the angular js file as the last element of the body.

<!DOCTYPE html> 
<html> 
   <head>
       <title>Contacts Angular Js</title>
   <link href="media/css/libs/bootstrap.min.css" rel="stylesheet">
   <link href="media/css/libs/bootstrap-theme.min.css" rel="stylesheet">
   </head>
   <body>
       <div id="page_content">
           <h1>Contacts</h1>
       </div>
   <script src="media/js/libs/angular.min.js"></script>
   </body>
</html>

Code Organization

In Angular Js we can organize our code in a number of different ways. As software developers we know the importance of good separation of code artifacts. By following standards and practices in organizing our code we can make our code easier to understand, maintain, and reuse. For this example we will organize the code per feature. We will talk more about this later, but for now it is enough to know that we consider contact a separate feature, if we had login functionality we would consider that a separate feature. For each feature we create a separate directory to store our code. By organizing our code by feature we make it easier to reuse our modules in different applications.

Angular Js Modules

As we mentioned we can organize our code by feature. Modules allow us to implement this separation in code. Modules can be considered similar in a way to a Javascript namespaces, java packages, c# namespaces etc. Modules act as containers for components. Modules, are an important concept in Angular JS they allow us to logically organize, and reuse our code through module dependencies. When we create an Angular JS application we normally create a root module, and include as dependencies any modules we wish to include in the application. These dependencies tell Angular JS how are application is composed.

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

The above statement creates a new app module. For the second argument we pass a list of our module dependencies. Above we pass an empty array as we currently have no dependencies.

Contact Module

Let’s create the contact module. Add a file called contactModule.js to the contact folder. This file will define the contact module that can be imported into our application.

angular.module('contacts', []);

Above we define a contacts module with no dependencies. For simplicity all of the components such as : controllers, services, models etc will be added to this module. We will import this module into our application by adding it as a dependency to the app module. Modify app.js and add the following:

angular.module('app', ['contacts']);

With the above code in place we have defined how our application is composed. What is interesting about the above is that no child dependency has knowledge of it’s parent. For example, the contact module has no knowledge of the root module – the app module. This mechanism facilitates code reuse, we can import the contact module into any application. Finally we will add the following empty files to the contact/src folder: controller.js, models.js and service.js

Bootstrap the application

With the above code in place we can now bootstrap angular JS. Lets modify the index.html file we created earlier.

<!DOCTYPE html> 
<html> 
   <head>
       <title>Contacts Angular Js</title>
       <link href="media/css/libs/bootstrap.min.css" rel="stylesheet">
       <link href="media/css/libs/bootstrap-theme.min.css" rel="stylesheet">
       <link href="media/css/app.css" rel="stylesheet">
   </head>
   <body ng-app='app'>


   <script src="media/js/libs/angular.min.js"></script>
   <script src="media/js/modules/contact/src/contactModule.js" ></script>
   <script src="media/js/app.js"></script>
   <script src="media/js/modules/contact/src/models.js" ></script>
   <script src="media/js/modules/contact/src/service.js" ></script>
   <script src="media/js/modules/contact/src/controller.js" ></script>

   </body>
</html>

Above we import all of js files just before the body tag. Also we add a ng-app=’app’ attribute to the body tag. The ng-app attribute is a special Angular JS directive that loads / bootstraps the application. The Angular JS run-time will load the app module, and following all of the dependencies to create the application.

Contact Model

Let’s briefly discuss the data that our model will use. When working with object orientated code we normally define objects that contain both state and behavior. The state would contain the objects data, and the behavior defines the functionality the object provides. There are different approaches to modeling data in Angular JS from thin, fat and hybrid approaches. For this example we will define our models as value services. We will talk more about services later but for now it is enough to know that a value service allows us to create modifiable objects via a function call.

(function () { 
   var Contact = function(data) {
       var self = this;
       self.id = null;
       self.firstName = '';
       self.lastName = '';
       self.email = null;

       self.update(data);
   };

   Contact.prototype = {
      contactName: function() {
           return this.lastName + " " + this.firstName;
      },
      update: function(data) {
          this.id = data.id || null;
          this.firstName = data.firstName || '';
          this.lastName = data.lastName || '';
          this.email = data.email || null;
      }
   }
   angular.module('contacts').value('ContactModel',Contact);
})();

Above we create our contact model. We also add a function called contactName to generate a calculated value for the contact name and an update function to set the contacts data. We enclose the function in an anonymous function that is immediately invoked. The anonymous function is used to isolate our code. We could have also isolated our model using the angular.module function.

angular.module('contacts').value('ContactModel',Contact);

The above code is similar to what we saw before when creating our module definitions. However, on this occasion we don’t pass module dependencies. When using the function in this way Angular js returns the module object. This allows us to add our model to the contacts module. We add our module to the contacts module as a value service. This will allow us to create contact models within our code using the form:

new ContactModel({ "id": 1,  "firstName": "John", "lastName": "Doe", "email": "johnj.doe@test.com"})

Contact Service

Next we will create our service. A service is different with regards to what we consider a service in other frameworks. In Angular js we consider a service to encapsulate common functionality, that can easily be reused. For example we can create a service for abstracting communication with a server. The Angular js library provides different examples of services. For example, it provides a $interval service which is similar to the javascript setTimeout function. We will create a contact service which we can consider as a Crud service for managing the contact data.

(function () { 
   var ContactService = function(ContactModel) {
       var self = this;
self.contacts = [];
       self.id = 1;

function init() {
           self.contacts.push(new ContactModel({ "id": createId(),  "firstName": "John", "lastName": "Doe", "email": "johndoe@test.com"}));
    self.contacts.push(new ContactModel({ "id": createId(),  "firstName": "Stephen", "lastName": "Patrick", "email": "stephenpatrick@test.com"}));



       };

       function list() {
           return self.contacts;
       };

       function addContact(contact) {
           contact.id = createId();
           self.contacts.push(contact);
       };

       function createId() {
           return self.id++;

       };

       init();


       var service = {
            list: list,
            add: addContact
       };

       return service;
   };

    angular.module('contacts').factory('contactService' ,ContactService);
})();

Again we create our service using the same practices as we did when we created our contact model. However, this time we create our service as a factory service. This enables us to decide what api we want to make available to the client. Also take note of our service constructor function:

var ContactService = function(ContactModel) {

Our ContactService constructor function above takes our ContactModel. If we remember we created the ContactModel as an Angular Js value service. When Angular js creates the contact service it will give the ContactModel service to the Contact service using its dependency injection mechanism. We can then use the ContactModel service within our code. Above we use it to create new contact instances.

Contact Controller

So now that we have our code in place to bootstrap the application and manage our data. We will create our contact controller. We can think of a controller as a component that coordinates the interaction between the view and the model – the data that the view displays. A controller also handles the events triggered from the UI. For those familiar with Knockout JS, a controller can be likened to that of a view object. The controller is a constructor function that defines data and behavior that can be accessed from a view if defined in such a way.

A very important concept in Angular JS is that of scope. We will talk more about scope later, but for now we can think of scope as the place in which variables can be referenced. When the Angular JS application bootstraps the application it creates a root scope. Then each time it creates a controller it will create a new scope giving the new scope access to its parent scope. For example, if we define variables in our root scope when this root scope creates a controller it will create a new scope passing all of the data defined in the root scope. Scopes can also be nested, for example if we define a nested controller inside of a parent controller then when Angular JS creates the nested controller it will create a new scope passing the scope of the parent controller. In this way we can access all of the data and functionality made available by the parent controller in the nested controller. So lets create our contact controller.

angular.module('contacts').controller('ContactController',function($scope,contactService, ContactModel) { 
   var self = this;
   self.contactService = contactService;
   self.selectedContact = null;
   self.editingContact = null;


   self.contacts = function() {
       console.log("CON");
       return self.contactService.list();
   };

   self.editing = function() {
       return self.editingContact !=null;

   };

   self.addContact = function(contact) {
       setSelectedContact(new ContactModel({}));
   };

   self.editContact = function(contact) {
       setSelectedContact(contact);
   };


   self.saveContact = function() {
       if(!self.editing) {
           return;
       }

       if(!self.editingContact.id) {
           saveNewContact();
       }
       else {
           updateContact();
       }

       setSelectedContact(null);
       resetForm();

   }

   self.cancelEdit = function() {
        resetForm();
   };

   function saveNewContact() {
       var newContact = angular.copy(self.editingContact);
       self.contactService.add(newContact);
   }

   function updateContact() {
        self.selectedContact.update(self.editingContact);
   };

   function setSelectedContact(contact) {
       self.selectedContact = contact;
       self.editingContact = angular.copy(self.selectedContact);
   }

   function resetForm() {
       self.crudForm.$setPristine();
       self.crudForm.$setUntouched();

       self.selectedContact =null;
       self.editingContact = null;
   }
});

Above we take advantage of dependency injection to have Angular js pass our contact service, and contact model service to our controller. It is also important to note that Angular js will only create a single instance of a service.

angular.module('contacts').controller('ContactController',function($scope,contactService, ContactModel)

Above we see the controller constructor function with parameters for the contact and model services that will be injected by Angular js. There is also a parameter named $scope. As discussed earlier, this is an important concept in Angular js. We can use the $scope parameter to make data available to the view. Any variables that we add to the scope can be referenced within our view. However, above we do something a little different. We add the properties that we would like to make available to the view by adding them to the controller itself, by adding them to the this reference. This is achieved using the controller as mechanism provided by Angular js that we will discuss shortly.

Let’s update our view binding the controller to display the data. Add the following code to the body tag.

       <nav class="navbar navbar-inverse">
           <div class="container">
               <div class="navbar-header">
                   <a href="" class="navbar-brand">Contact Application</a>
               </div>
           </div>
       </nav>
       <div id="page_content" class="container">
           <div class="container" ng-controller="ContactController as contactController">
 <h1>Manage Contacts</h1>
        <p>From here you can add and edit contacts</p>
               <div class="block_row clearfix">
                   <button type="button" class="btn btn-default pull-right" ng-click="contactController.addContact()">
                       <span>Add Contact</span>
                   </button>
               </div>
               <div class="panel panel-default" ng-if="contactController.editing()">
                   <div class="panel-heading">Add Contact</div>
                       <div class="panel-body">
                           <form id="crud-form" name="contactController.crudForm">
                               <div class="form-group">
                                   <label for="inputFirstName">First Name</label>
                                    <input type="text" class="form-control" id="inputFirstName" placeholder="Enter First Name" ng-model="contactController.editingContact.firstName" ng-required="true" ng-minlength="3" ng-maxlength="255">
                               </div>
                               <div class="form-group">
                                   <label for="inputLastName">Last Name</label>
                                    <input type="text" class="form-control" id="inputLastName" placeholder="Enter Last Name" ng-model="contactController.editingContact.lastName" ng-required="true" ng-minlength="3" ng-maxlength="255">
                               </div>
                               <div class="form-group">
                                   <label for="inputEmail">Email</label>
                                    <input type="email" class="form-control" id="inputFirstName" placeholder="Enter Email" ng-model="contactController.editingContact.email" ng-required="true" ng-minlength="3" ng-maxlength="255">
                               </div>
                               <div class="form_footer clearfix">
                                   <button class="btn btn-default pull-right" ng-click="contactController.saveContact()">Save</button>
                     <button class="btn btn-default pull-right" ng-click="contactController.cancelEdit()">Cancel</button>
                               </div>

                           </form>
                       </div>
               </div>

               <div class="panel panel-default">
                   <div class="panel-heading">Manage Contacts</div>
                       <div class="panel-body">
                           <table class="table table-striped table-bordered">
                               <thead>
                                   <tr>
                                       <th>Contact Name</th>
                                       <th>Email</th>
                                   </tr>
                               </thead>
                               <tbody>
                                   <tr ng-repeat="contact in contactController.contacts()" id="tr-{{contact.id}}" class="actionable" ng-click="contactController.editContact(contact)">
                                       <td>{{contact.contactName()}}</td>
                                       <td>{{contact.email}}</td>
                                   </tr>
                              </tbody>
                           </table>
                       </div>
               </div>

           </div>
       </div>

If we were to open the index.html file in the browser we would see the following.

In order for us to bind our controller to the view we use the ng-controller directive.

<div class="container" ng-controller="ContactController as contactController">

This directive enables us to access the properties of the contact controller from within the div tag. It is also important to note the as ContactController “controller as” syntax. This allows us to bind the properties directly on the controller object within our controller definition. When Angular js finds this directive it will create a new scope and pass it to our controllers constructor function via the $scope parameter. Any variables created within this scope inside the controller or its parent scope can be accessed via the controller instance.

<tr ng-repeat="contact in contactController.contacts()" id="tr-{{contact.id}}" class="actionable" ng-click="contactController.editContact(contact)"> 
   <td>{{contact.contactName()}}</td>
   <td>{{contact.email}}</td>
</tr>

The above code uses the Angular Js ng-repeat directive to loop through our contacts data model provided by the contacts controller contacts() property. For each contact the ng-repeat directive will repeat the table row html code. To display the contact model data within the table cells we use a special syntax to bind the current contact that is made available by the repeat directive to the html element.

{{contact.contactName()}}

Add Contact

We also take advantage of the event mechanism provided by Angular Js to add and edit our contact data. To achieve this we will use a form to add a new contact, and edit an existing contact. To add a new contact we will use an add button that when clicked will trigger an event and call the addContact() function of our controller to create a new contact.

<button type="button" class="btn btn-default pull-right" ng- click="contactController.addContact()"> 
   <span>Add Contact</span>
</button>

Above we use the ng-click directive to bind the click event to our controllers addContact function.

self.addContact = function(contact) {
   setSelectedContact(new ContactModel({}));
};

function setSelectedContact(contact) { 
       self.selectedContact = contact;
       self.editingContact = angular.copy(self.selectedContact);
}

This function will create a new contact and set it as the selected contact. This will trigger our contact form to be displayed.

<div class="panel panel-default" ng-if="contactController.editing()">  </div>

We use the ng-if directive to display the contact form. If the result of the editing function is true the form will be displayed, otherwise it will be hidden. If the user wishes to cancel editing they can use the cancel button.

<button class="btn btn-default pull-right" ng- click="contactController.cancelEdit()">Cancel</button>

When clicked this button will trigger the cancelEdit function of the controller. This function will remove the selected contact and hide the form.

self.cancelEdit = function() { 
   resetForm();
};
function resetForm() { 
   self.crudForm.$setPristine();
   self.crudForm.$setUntouched();
   self.selectedContact =null;
   self.editingContact = null;
}

Edit Contact

Editing works in the same way. We add an ng-click directive to the table row displaying the contact. When we click the table row it will display the contact form populating the form fields.

<tr ng-repeat="contact in contactController.contacts()" id="tr-{{contact.id}}" class="actionable" ng-click="contactController.editContact(contact)">

The editContact function will set the contact we clicked as the selected model. We also make a copy of the contact model, so we can undo the changes if we decide to cancel the edit operation. The editContact function calls the setSelectedContact(contact) function to setup and display the form for editing.

We bind our form fields data using the ng-model directive.

<input type="text" class="form-control" id="inputLastName" placeholder="Enter Last Name" ng-model="contactController.editingContact.lastName" ng-required="true" ng-minlength="3" ng-maxlength="255">

This directive will use the property that we wish to bind of the editing contact model to the form field, in the case above, we are binding the lastName property.

When editing is complete the user can click the save button which will trigger the saveContact function of the controller.

self.saveContact = function() { 
       if(!self.editing) {
           return;
       }

       if(!self.editingContact.id) {
           saveNewContact();
       }
       else {
           updateContact();
       }

       setSelectedContact(null);
       resetForm();
}
function saveNewContact() { 
       var newContact = angular.copy(self.editingContact);
       self.contactService.add(newContact);
} 
function updateContact() { 
        self.selectedContact.update(self.editingContact);
};