Angularjs directive development is easy

Original author: Sean Hess
  • Transfer
  • Tutorial

AngularJS directives are cool


AngularJS is a framework for building web applications, which allows you to create complex applications quite simply. One of his best features is to create directives that are reusable web components. This makes it possible to create new HTML tags and attributes that can dynamically display content in response to data changes, and update the data itself, if necessary.
This is a very high-performance approach because it allows you to wrap complex DOM interactions in reusable code packages.

In the beginning, creating directives seems confusing.


A little time will pass and you will understand how useful the directives are. The directives built into AngularJS are a great example of their development. But at first, when creating directives, there may be some difficulty in understanding their work. The Angular team did a good job creating the directives extremely flexible and powerful, although all this power is given to the beginner not without difficulty.
In particular, it is difficult to understand how to create a directive that would respond to data changes, modify data, respond to certain events, or excite them. It basically boils down to one question:
How do I interact with the directive?
This article is intended to explain and simplify some of the most common problems that may arise when creating directives.

Principles for creating directives


Directives will only make your life easier if you can use them without having to read and modify the source code. In this case, you can forget how they work, but just know what they are doing.
If you have previously used one of the presentation-oriented frameworks such as Backbone , you might want to split your application into small pieces using directives. For example, if you want to display a list of users, you can create a directive that will read $scope.usersand display them in a view:

The user-list directive works. Note that its use is in accordance with the principle of “do not repeat” ( DRY )! However, let's compare it with ng-repeat , which only handles repetitions. Which of them can be reused in different places? What if you need to display users differently in two places?
A good directive does one thing
ng-repeat better than user-listbecause it is responsible for only one action: It only repeats a certain part, so that it can be used in many situations. It’s easy to understand what she is doing. Instead of making one directive that is responsible for everything, it’s better to break it down into several directives that will perform specific tasks, and use them together.
A good directive does not depend on the specifics of the application.
Directives are all the more useful, the less they make assumptions about the application. A directive that allows the user to specify which property to observe, such as ng-model, is more useful than a directive that assumes that $scope.usersexists. As a rule, if your directive should be used in various applications, it should follow this rule so that you can say that it is well designed, even if you are not going to publish it.
For today, enough theory. Let's dive into some specific examples that demonstrate common ways to interact with directives.

How to display anchors


The first thing you need to know is how to make a directive that will display the value of the bound property: since this is done with double curly braces. For example, let's make a directive that will display a photo and a caption to it.
The first step in designing any directive is choosing names for the attributes that will represent it in your interface. I chose photo-srcto indicate the source of the image, and captionfor the signature. Be careful not to use names already used by other directives, such as ng-srcif you do not know how they work.
Secondly, you need to decide whether you will only support attributes and class names, or whether you will also support elements. In our case, we want to photobe an element.

Please note that I did not pass the object of the photograph completely to the directive. This solution allows you to better adapt the directive to work with a different data structure.
To read the values ​​of bound properties, used attrs.$observe. In this case, the callback function will be called every time the value of the bound property is changed. Then we use elementto make changes to the DOM.
app.directive('photo', function() {
    return {
        // обязательно, для поддержки работы через элемент
        restrict: 'E',
        // заменить  этим html
        template: '
', replace: true, // наблюдение и манипулирование DOM link: function($scope, element, attrs) { attrs.$observe('caption', function(value) { element.find('figcaption').text(value) }) // атрибуты именуются с применением «верблюжьей» нотации attrs.$observe('photoSrc', function(value) { element.find('img').attr('src', value) }) } } } })

In addition, if your component has its own template, you can do all this in an isolated scope.
app.directive('photo', function() {
    return {
        restrict: 'E',
        templateUrl: 'photo.html',
        replace: true,
        // передача двух атрибутов из attrs в область видимости шаблона
        scope: {
            caption: '@',
            photoSrc: '@'
        }
    }
})

{{caption}}


How to read and write data


Some directives should also write data, for example ng-model.
Let's make a directive for the switch button. This directive will automatically set the state of the switch, depending on some logical value in the scope, and when you click on the button, it will change the state to the opposite.
When transferring data in this way, you do not need to use curly braces, you use "expressions". An expression is javascript code that will be executed in a specific scope. Expressions can be used in all cases when you need to write data, or when an object or an array is passed to the directive, instead of a string.

First we use =in scope: to make scope.toggleavailable in our directive. Although this is not explicitly specified anywhere inside the directive, using this syntax scope.togglereads and writes the property that the user specified in the attribute.
app.directive('toggle', function() {
    return {
        scope: {
            toggle: '=',
        },
        link: function($scope, element, attrs) {

Then we use scope.$watch, which performs the function passed to it each time the value of the expression changes. We will add or remove the css class active, inside the handler called upon changes.
            $scope.$watch("toggle", function(value) {
                element.toggleClass('active', value) 
            })

In the end, let's subscribe to an event clickwhere we will update the scope. We need to use it scope.$applyevery time changes occur outside the Angular execution context.
            element.click(function() {
                $scope.$apply(function() {
                    $scope.toggle = !$scope.toggle
                })
            })
        }
    }
})

Working demo

How to exhibit events


In some cases, the controller is required to respond to events occurring inside the directive, for example, as in ng-click. Let's make a directive scrollthat can call a function when the user scrolls an element. Other than that, let's also handle the scroll offset value.
...
Like a switch button, we pass any function specified in the attribute scrollto the scope of our directive.
app.directive('scroll', function() {
    return {
        scope: {
            scroll: "&"
        },
        link: function($scope, element, attrs) {

We will use the jQuery scroll event to get the behavior we need. You also need to call here scope.$apply, because although the handler is called, it is not called in the context of the controller.
            element.scroll(function() {
                $scope.apply(function() {
                    var offset = element.scrollTop()
                    $scope.scroll({offset:offset})
                })
            })
        }
    }
})

Please note that we do not pass the offset value in the first parameter, we pass the hash of the available parameters, and make them available inside the expression onScroll(offset)that was passed through the attribute. This is a much more flexible approach than passing parameters directly, since other parameters of the scope can be transferred to the corresponding functions, for example, the current element in ng-repeat.
Working demo

How to get HTML content


Directives can have any html content, but as soon as you specify a template, their content changes to it.
Let's create a component modal: a pop-up window with a close button, for which you want to save its contents specified in html.

Some contents

Put whatever you want in here


Our item modal consists of more than one item. When we make a template, we insert all the received content into it, where necessary, simply by adding a special directive ng-transcludeto div.

Passing content from a directive to a template is very easy to enable. To do this, simply install transclude: true:
app.directive('modal', function() {
    return {
        restrict: 'E',
        templateUrl: 'modal.html',
        replace: true,
        transclude: true,
    }
})

To achieve more complex results, you can combine any methods from this article.

How to respond to events


Sometimes it may require calling a function in your directive when a certain scope event occurs. For example, you might want to close an open modal window if the user presses the escape key.
This almost always means that you pay too much attention to events, although you should think about data flow. Controllers not only contain data, they also contain view state. It is normal practice to have a boolean variable windowShownin the controller that you bind to using ng-show, or pass a boolean value to your directive, in the manner described above.
There are cases when it makes sense to use$scope.$onin the directive, but for starters, instead, try to think about the problem in terms of state changes. In Angular, things get a lot easier if you focus on data and state instead of events.

Additional Information


Directives can do a lot more. But all of these additional features are not covered in this article. Please visit the directives documentation page for more information.

Also popular now: