AngularJS Tutorial: Part 2

In the previous part, we developed a web application that actually does nothing. Following is a brief description of what we've done so far:

- Created a HTML template for our application
- Added AngularJS and twitter-bootstrap libraries and integrated with our application
- Tested AngularJS integration by writing an Angular expression

In this post, we are going to modify the left navigation table so that clicking on an item keeps it in selected state i.e add blue background to mark as selected.



In order to do so, we are going to use Directives. So, first question is what is a directive? Here is a high level description from AngularJS documentation

At a high level, directives are markers on a DOM element (such as an attribute, element name, comment or CSS class) that tell AngularJS's HTML compiler ($compile) to attach a specified behavior to that DOM element or even transform the DOM element and its children.

As you probably know, AngularJS has a HTML compiler. When browser loads the DOM, AngularJS parses the DOM, finds out different components and glue them accordingly.

Directives are a way to define a reusable component of our application that is associated with an UI component and encapsulates the logical behaviors associated with that component. Examples of where directives can be used are date picker, drop-down list, navigation menu etc.

In our application, we want to define a navigation panel which is an UI element and clicking on an item will mark it as selected which is a logical behavior associated with that UI component only. Hence we can use directive here.

Enough said! Now let's look at the codes that we need to write to have our directive. Here is the changes what we need to make : Changes in Github for Part 2

As you can see, two files have been changed - index.html and app.js

In app.js, we've declared a directive. Here is the syntax to declare a directive

angular.module('app', []).directive('apiList', function(){ // return directive object here });
Here, app is the name of the application which we've added inside the ng-app in index.html. Inside the directive function, we've to give a name to our directive i.e apiList in our case and provide a function which will be responsible for returning the directive. This function have to return an object containing different components of a directive. Here is a sample directive with some data:

angular.module('app', []).directive('apiList', function(){ return {
scope: {},
replace: true,
controller: function($scope, $element) { // body here },
template: '<h1>I'm a directive!</h1>'
}; });

A very short explanation on these components:

- The scope works as namespace of this directive. That means, anything e.g method, variable etc declared inside the scope will be accessible within this directive only. If we need any data or method that is associated with our directive, we've to put it inside scope.
- replace=true says the html tag where this directive has been used will be replaced by the directive UI template. 
- controller is a function that receives the scope and UI element reference. It can change the scope data. It also can define functions inside the scope that will be accessible through the directive only.

Please refer to the Directive documentation to explore different attributes of a Directive and how to use them.

Let's look at our controller. Here is our full code of directive:

.directive('apiList', function() {
    return {
      scope: {},
      replace: true,
      controller: function($scope, $element) {
        var panes = $scope.panes = [];

        panes.push({'title':"Item 1", "selected":false});
        panes.push({'title':"Item 2", "selected":false});
        panes.push({'title':"Item 3", "selected":false});
        panes.push({'title':"Item 4", "selected":false});
        panes.push({'title':"Item 5", "selected":false});
 
        $scope.select = function(pane) {
          angular.forEach(panes, function(pane) {
            pane.selected = false;
          });
          pane.selected = true;
        }
      },
      template:
          '<ul class="nav nav-sidebar">' +
            '<li ng-repeat="pane in panes" ng-class="{active:pane.selected}">'+
              '<a href="" ng-click="select(pane)">{{pane.title}}</a>' +
            '</li>' +
          '</ul>'
    };
  });



In controller, we have defined an array named panes inside the scope. After that, we've inserted 5 items in the array. Each item has two properties - title and selected. Title is the string that'll be displayed and selected is a boolean field that represents whether this item is selected or not.

We've declared a function named select in the scope. This function takes an item as it's argument and marks it as selected. Before that, it also marks all other items as not selected.

In the template, we've declared a ul item and a li item inside it. The ng-repeat directive tells that li item will be repeated for each pane in panes array. So for each item, there'll be a li element. In our case, there'll be 5 li elements.

In li elements, we're using another directive ng-class

ng-class="{active:pane.selected}"

This directive will set attribute class="selected" if the condition after : is true. So, in our case, if current pane object has selected=true, it'll have class="selected" attribute.

Inside the li element, there is a hyperlink element as described below

{{pane.title}}

This element has another directive ng-click. This directive will call the declared function when there is a click event on that element. In our case, when there is a click event on the anchor element, the select function of the directive will be called and corresponding pane object will be passed to it.


In the index.html, there are two changes. By adding ng-app="app", we've given a name to our AngularJS application module.

In the left panel, we've removed the previous ul element and added

<ul api-list></ul>;

Here we're using the directive. Whenever AngularJS sees the api-list, it searches for a directive named apiList. After that, it'll run the controller function and render the template with scope. If it sees replace=true in that directive, it'll replace the element which is declared in the index.html with the rendered element. If replace=false, it'll append the rendered element inside the declared element.


Now, if we open the index.html file in a browser, we'll see that clicking on an item marks it as selected!

Here is the final source code in this step : Github link for Part 2

Previous Posts:
Part 1
Part 0


Comments

Popular posts from this blog

Run tasks in background in Spring

How to configure Wildfly 10 to use MySQL

Conditional field inclusion in Jackson and Spring Boot