Angular directives

Simply put, directives are custom HTML elements. You write them like ordinary HTML elements, but their functionality is defined entirely programmatically. Thus, they extend the standard HTML syntax by letting us add whatever we need to it in order to build truly dynamic pages.

Creating directives

Like the services and controllers that we have already seen, directives are defined as components of modules, and AngularJS gives us the tools that are necessary to create them. We will explore this process by creating a directive for the map that we created earlier.

The first thing that we will need to do is create a new file named www/js/directives.js for the directives of our project. Create this file and add the following to it:

angular.module('supernav.directives', [])
.directive('map', function () {
  return {}
});

The directive module function is used to define a directive for a module, and as you might have guessed, its first parameter is the name of the directive itself, while the second one is a factory function that gives us an object describing how the directive works. In that sense, directives are similar to the services that we studied earlier.

Restricting directives

Let's start building the factory function for our map directive. The first thing that we should do is add a restriction to the directive in order to tell the AngularJS parser which kinds of HTML elements this particular directive may occur as:

angular.module('supernav.directives', [])
.directive('map', function () {
  return {
    restrict: 'E'
  }
});

Right now, you are probably exclaiming, E? What is this E of which you speak? Well, AngularJS allows us to confine a directive to the following three different classes of elements:

  • E (Elements): These are your standard HTML tags, such as <map></map>
  • A (Attributes): These are the element attributes, such as <div map></div>
  • C (Classes): These are the customized element class attributes that are mapped to the directive, such as <div class="map"></div>

You are not required to stick with just one restriction. For example, you can also write the following in order to restrict it to elements and attributes:

angular.module('supernav.directives', [])
.directive('map', function () {
  return {
    restrict: 'EA'
  }
});

Hence, the Angular parser will detect the directive if you write either <map></map> or <div map></map>.

Note

You will frequently find that it makes sense to restrict directives only to a single kind of element. This is good practice as it reduces the complexity of your app.

Scope isolation

Just like controllers, directives are able to access the scope in which they are operating. However, it is also possible (and generally considered good practice) to create an isolated scope for the directive. This scope will contain a set of data that only the current instance of the directive is aware of. In addition to this, scope isolation also helps you create reusable widgets, which enhance code quality.

We achieve this by defining scope injection points in our directive, which will take the form of the standard HTML attributes:

angular.module('supernav.directives', [])
.directive('map', function () {
  return {
    restrict: 'E',
    scope: {
      onCreate: '&'
    }
});

Here, we defined an injection point called onCreate, which maps the directives to a function in the parent scope that we are isolating (the & symbol signifies a binding by delegation). For example, let's say that we want to inject the onCreate method from MapCtrl into the isolated scope. We will then write our directive like this:

<map on-create="mapCreated(map)"></map>

At this point, the map parameter is not bound. Later, we will see how to define and pass it to the function from within the directive itself in the next section.

However, before we move on, did you observe that although we name our injection point onCreate, we wrote it as on-create in the actual HTML? This is due to an AngularJS process called normalization. Through this, attributes and tags are translated into a more concise form. Part of the process involves replacing hyphen-bound words with camel-cased words. We will give you the reference to the documentation if you wish to know more about how it works, since understanding it is not crucial to developing our directive here.

DOM manipulation

Ultimately, we want our map directive to expand and show a map where it occurs in the DOM. To do so, we will need to allow it to actually manipulate the DOM itself.

The typical way to achieve this is by providing the directive with a link function, which allows it to look into the DOM update process. Let's add one link to our map directive, as follows:

angular.module('supernav.directives', [])
.directive('map', function () {
  return {
    restrict: 'E',
    scope: {
      onCreate: '&'
    },
    link: function ($scope, $element, $attr) {
      function initialize() {
        var mapOptions = {
          center: new google.maps.LatLng(43.07493, -89.381388),
          zoom: 16,
          mapTypeId: google.maps.MapTypeId.ROADMAP
        };
        var map = new google.maps.Map($element[0], mapOptions);

        $scope.onCreate({map: map});

        google.maps.event.addDomListener(
          $element[0], 'mousedown', function (e) {
            e.preventDefault();
            return false;
        });
      }

      if (document.readyState === "complete") {
        initialize();
      } else {
        google.maps.event.addDomListener(window, 'load', initialize);
      }
    }
  }
});

Looks oddly familiar, doesn't it? This is the same initialize function and associated map setup procedure that we defined in our controller earlier, albeit with some slight modifications. We have already covered how this works. So, let's go over how it figures in the context of the link function:

The link function takes the following three parameters:

  • $scope: This is the scope under which the directive is rendered.
  • $element: This is the tag to which the directive is bound, which is <map> in our case. The tag is wrapped in the JQuery-like jqLite library, which allows us to perform direct manipulations on it.
  • $attr: This defines the attributes for the directive element along with their associated values.

Inside the initialize function itself, we now use $element[0] in order to get the name of the tag itself (map in our case). We also use the $scope parameter in order to call the onCreate delegate in the parent scope (note how we explicitly need to define the parameter name and its associated value in this case).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.70.170