⬆️ ⬇️

Developing angularjs directives is just

AngularJS directives are cool



AngularJS is a framework (framework) for building web applications, which allows you to create complex applications quite simply. One of its best features is the creation of 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 interactions with the DOM into reusable code packages.



At the beginning, making directives seems confusing.



It will take a little time, and you will understand how useful directives. AngularJS built-in directives are a great example of their design. But at first, when creating directives, some difficulties with understanding their work are possible. The Angular team did a good job, creating 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 react to a change in data, change data, react to certain events, or initiate them. Basically it comes down to one question:

How do I interact with the directive?

This article aims to explain and simplify some of the most common problems that may arise when creating directives.



Principles for creating directives



Directives will make your life easier only if you can use them without having to read and change 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 view-oriented frameworks, such as Backbone , you may 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.users and display them in a view:

 <user-list/> 


The user-list directive works. Note that its use complies with the principle "Do not repeat" ( DRY )! However, let's compare it with ng-repeat , which only processes 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 the user-list because it is responsible for only one action: It only repeats a specific part, so it can be used in many situations. Easy to understand what she does. Instead of making one directive that is responsible for everything, it is better to break it into several directives that will perform specific tasks, and use them together.

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 the ng-model , is more useful than a directive that assumes that $scope.users exists. 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 developed, even if you are not going to publish it.

There is enough theory for today. Let's dive into some concrete examples that demonstrate common ways of interacting with directives.

')

How to display bindings



The first thing you need to know is how to make a directive that will display the value of the associated property: since this is done with double curly braces. For example, let's make a directive that will display a photo and a caption for it.

The first step in designing any directive is choosing the names for the attributes that will represent it in your interface. I chose photo-src to specify the source of the image, and caption for the signature. Be careful not to use names already used by other directives, such as ng-src , if you do not know how they work.

Second, you need to decide whether you will only support attributes and class names, or also support elements. In our case, we want the photo be an element.

 <photo photo-src="{{photo.url}}" caption="Taken on: {{photo.date}}"/> 


Please note that I did not pass the photo object to the directive completely. This solution makes it possible to better adapt the directive to work with another data structure.

To read the values ​​of bound properties, use attrs.$observe . In this case, the callback function will be called each time the value of the associated property is changed. Then we use element to make changes to the DOM.

 app.directive('photo', function() { return { // ,      restrict: 'E', //  <photo>  html template: '<figure><img/><figcaption/></figure>', 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: '@' } } }) 


 <!-- photo.html --> <figure> <img ng-src="{{photoSrc}}"/> <figcaption>{{caption}}</figcaption> </figure> 




How to read and write data



Some directives also need to 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 a certain logical value in the field of view, 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.

 <!--     --> <button toggle="preferences.showDetails">Show Details</button> 


First we use = in scope : to make scope.toggle available in our directive. Although it is not explicitly specified anywhere inside the directive, using this syntax, scope.toggle reads 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 every time the value of the expression changes. We will add or remove the active class css, inside the handler called during changes.

  $scope.$watch("toggle", function(value) { element.toggleClass('active', value) }) 


In the end, let's subscribe to the click event, where we will update the scope. We need to use scope.$apply every time changes occur outside of the Angular execution context.

  element.click(function() { $scope.$apply(function() { $scope.toggle = !$scope.toggle }) }) } } }) 


Work demo



How to expose events



In some cases it is required that the controller reacts to events occurring within a directive, for example, as in ng-click . Let's make a scroll directive that can call a function when the user scrolls an element. In addition, let's also handle the scroll offset value.

...

Similar to a switch button, we pass any function specified in the scroll attribute to 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. Here you also need to call 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}) }) }) } } }) 


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 within the onScroll(offset) expression that was passed through the attribute. This is a much more flexible approach than passing parameters directly, since other scope parameters can be passed to the corresponding functions, for example, the current element in ng-repeat .

Work 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 modal component: a pop-up window with a close button for which you want to save its contents specified in html.

 <modal> <p>Some contents</p> <p>Put whatever you want in here</p> </modal> 


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

 <div class="modal"> <header> <button>Close</button> <h2>Modal</h2> </header> <div class="body" ng-transclude></div> </div> 


Transferring content from a directive to a template is very simple. 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 to call a function in your directive when a certain scope event occurs. For example, you might want to close an open modal window if a user presses the escape key.

This almost always means that you pay too much attention to events, although you need to think about the flow of data. Controllers not only contain data, they also contain view state. It is normal practice to have a logical windowShown variable in the controller, to which you bind using ng-show , or transfer the logical value to your directive, in the manner described above.

There are cases when it makes sense to use $scope.$on in a directive, but for a start, instead, try to think about the problem in terms of state changes. In Angular, things get much easier if you focus on data and state, instead of events.



Additional Information



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

Source: https://habr.com/ru/post/200620/



All Articles