
In my opinion, directives are the main highlight of the Angularjs declarative style. However, if you open user comments in the Angularjs official documentation section
on directives , you will see that the
most popular one is : “Please rewrite the documentation, make it more accessible and structured. It’s hard for a novice developer on Angularjs to figure out ”(“ Please rewrite a clearer well structured documentation of directives., It is difficult to disagree with this, the documentation is still damp and in some moments it is necessary to make great efforts to understand the logic and essence of the functional. Therefore, I offer you my free retelling of this chapter in the hope that it will save time for some people, and I also count on your support and participation in the comments. So let's go!
How to write directives?
Directives in Angularjs are set together with other module configurations as follows:
angular.module('moduleName', []) .directive('directiveName', function () { }) .directive('anotherDirectiveName', function () { });
In this case, there are two options for their ads. Simple and more powerful long options.
Simple directive creation option
In order to write a directive that will be called when specifying HTML markup, in the simplest case, you need to set a certain function (it is called Linking
Linking , but more on that later) returned by the factory:
')
angular.module('moduleName', []) .directive('directiveName', function () { return function(scope,element,attrs){ } });
This function takes the following parameters:
- scope - the scope in which the directive is called
- element - the DOM element that owns the directive wrapped in jQuery Lite
- attrs - an object with a list of all attributes of the tag in which the directive is called
Let's take a more detailed example. Let's write such a directive (let's call
habra-habr ), which will add two lines and display inside the layout element in which it is called. At the same time, we will set one line as a controller variable (
forExampleController ), and the second line as an attribute (
habra ) in the same tag. And also reserve the opportunity to determine the name of the controller variable when calling a directive:
[
jsFiddle ]
<div ng-app="helloHabrahabr"> <div ng-controller="forExampleController"> <input ng-model="word"> <span habra-habr="word" habra="Nehabra"></span> </div> </div>
function forExampleController($scope) { $scope.word="Habrahabra" } angular.module('helloHabrahabr', []) .directive('habraHabr', function() { return function($scope, element, attrs) { $scope.$watch(attrs.habraHabr,function(value){ element.text(value+attrs.habra); }); } });
Everything. Directive in a primitive form, we are ready. You can go to a more detailed form.
Expanded version
In its full form, the directive assignment is as follows:
angular.module('moduleName', []) .directive('directiveName', function () { return { compile: function compile(temaplateElement, templateAttrs) { return { pre: function (scope, element, attrs) { }, post: function(scope, element, attrs) { } } }, link: function (scope, element, attrs) { }, priority: 0, terminal:false, template: '<div></div>', templateUrl: 'template.html', replace: false, transclude: false, restrict: 'A', scope: false, controller: function ($scope, $element, $attrs, $transclude, otherInjectables) { } } });
All these properties are rather closely connected and intertwined with each other. And in order to make it easier to figure this out, they are best viewed by certain semantic groups.
Link and Compile
The Link method is the same function that the directive factory returned in the short version. Here you need to understand that in Angularjs the compilation process is divided into two stages:
- compile - analysis of all directives used in this DOM element (including in its descendants child )
- linking - linking variables used in the template and variables in scope
And at the same time, both in the simplest version and in the extended method, the Link method will correctly call
postLink , since it is executed after the variables have already been mapped. Consider the examples.
First, I propose to rewrite an example of a simple directive in an extended manner.
[
jsFiddle ]
angular.module('helloHabrahabr', []) .directive('habraHabr', function() { return { link:function($scope, element, attrs) { $scope.$watch(attrs.habraHabr,function(value){ element.text(value+attrs.habra); } ); } } });
That is, everything really works as before. Now we can complicate the task and make our phrase not be output via direct interaction with the DOM
element.text (...) , but within the interpolate directive
"{{}}" :
[
jsFiddle ]
angular.module('helloHabrahabr', []) .directive('habraHabrNotwork', function() { return { link:function($scope, element, attrs) { element.html("<div>{{"+attrs.habraHabrWork+"}}"+attrs.habra+"</div>"); } } }) .directive('habraHabrWork', function() { return { compile: function compile(templateElement, templateAttrs) { templateElement.html("<div>{{"+templateAttrs.habraHabrWork+"}}"+templateAttrs.habra+"</div>"); }, link: function (scope, element, attrs) { } } });
Sample updated after tamtakoe comment
In the example above, the
habraHabrNotwork directive
will not work correctly, since we insert the "{{}}" directive with variables in the postLink, that is, when compilation and linking have already been done. In other words, Angularjs does not even know that "{{}}" is a directive that is enforceable.
Another thing, the second directive. There everything is in its place, we insert the template "{{" + attrs.habraHabrNotwork + "+" + attrs.habra + "}}" before compiling, and it is successfully rendered.
Let us dwell on the
compile method. It can return both the postLink function and an object with two parameters: pre and post. Where pre and post are the preLink and postLink methods, respectively. From the name of the methods it may seem that we are talking about methods before and after
Link a. But this is not entirely true; these functions are performed before and after the
Link and children directives in the DOM. For example:
[
jsFiddle ]
<div ng-app="helloHabrahabr"> <div ng-controller="forExampleController"> <input ng-model="word"> <span habra-habr-work="word" habra="NehabraParent"> <span habra-habr-work="word" habra="NehabraChild"></span> </span> <pre>{{log}}</pre> </div> </div>
function forExampleController($scope) { $scope.word="Habrahabra"; $scope.log=""; } angular.module('helloHabrahabr', []) .directive('habraHabrWork', function() { return { compile: function compile(templateElement, templateAttrs) { templateElement.prepend("<div>{{"+templateAttrs.habraHabrWork+"}}"+templateAttrs.habra+"</div>"); return { pre: function ($scope, element, attrs, controller) { $scope.log+=templateAttrs.habra +' preLink \n'; }, post: function ($scope, element, attrs, controller) { $scope.log+=templateAttrs.habra +' postLink \n'; } } } } });
On this I propose to make a pause. If the topic is interesting, in the coming days I will try to write a sequel about scopes and patterns.