Как писать директивы?
Директивы в Angularjs задаются вместе с другими конфигурациями модуля следующим образом:
angular.module('moduleName', []) .directive('directiveName', function () { /*Метод-фабрика для директивы*/ }) .directive('anotherDirectiveName', function () { /*Метод-фабрика для директивы*/ });
При этом есть два варианта их объявления. Простой и более мощный длинный варианты.
Простой вариант создания директивы
Для того, чтобы написать директиву, которая будет вызываться при указании в HTML разметке, в простейшем случае вам нужно задать некую функцию (она называется Связующей Linking, но об этом чуть позже), возвращаемую фабрикой:
angular.module('moduleName', []) .directive('directiveName', function () { return function(scope,element,attrs){ } });
Эта функция принимает следующие параметры:
- scope — область видимости, в которой вызывается директива
- element — элемент DOM, которому принадлежит директива, обернутый в jQuery Lite
- attrs — объект со списком всех атрибутов тэга, в котором вызывается директива
Давайте на более развернутом примере. Напишем такую директиву (назовем habra-habr), которая будет складывать две строчки и выводить внутри элемента верстки, в котором вызывается. При этом одну строчку мы будем задавать в качестве переменной контролера(forExampleController), а вторую передавать атрибутом(habra) в этом же тэге. А также оставим за собой возможность определять имя переменной контролера при вызове директивы:
[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) { /*Задаем функцию, которая будет вызываться при изменении переменной word, ее имя находится в attrs.habraHabr*/ $scope.$watch(attrs.habraHabr,function(value){ element.text(value+attrs.habra); }); } });
Всё. Директива в примитивном виде у нас готова. Можно переходить к более развернутой форме её задания.
Развернутый вариант
В своей полноценной форме задание директивы выглядит следующим образом:
angular.module('moduleName', []) .directive('directiveName', function () { return { priority: 0, template: '<div></div>', templateUrl: 'template.html', replace: false, transclude: false, restrict: 'A', scope: false, controller: function ($scope, $element, $attrs, $transclude, otherInjectables) { }, compile: function compile(temaplateElement, templateAttrs) { return { pre: function (scope, element, attrs) { }, post: function(scope, element, attrs) { } } }, link: function (scope, element, attrs) { } } });
Все эти свойства довольно тесно друг с другом связаны и переплетены. И для того, чтобы было проще в этом разобраться, их лучше рассматривать некими смысловыми группами.
Link и Compile
Метод Link это та самая функция, которую возвращала фабрика директивы в короткой версии. Здесь надо понять, что в Angularjs процесс компиляции разбит на два этапа:
- compile — анализ всех директив используемых в данном элементе DOM ( в том числе и в его потомках child)
- linking — связывание переменных используемых в шаблоне и переменных в scope
И при этом как в простейшей версии, так и в расширенной метод Link правильно будет называть postLink, поскольку он выполняется после того, как переменные уже сопоставлены. Рассмотрим примеры.
Сперва, я предлагаю переписать пример простой директивы на манер расширенной.
[jsFiddle]
angular.module('helloHabrahabr', []) .directive('habraHabr', function() { return { link:function($scope, element, attrs) { /*Задаем функцию, которая будет вызываться при изменении переменной word*/ $scope.$watch(attrs.habraHabr,function(value){ element.text(value+attrs.habra); } ); } } });
То есть всё, действительно, работает по-прежнему. Теперь можно усложнить задачу и сделать так, чтобы наша фраза выводилась не посредством прямого взаимодействия с DOM element.text(…), а внутри директивы interpolate "{{}}":
[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>"); return function (scope, element, attrs) { } } } });
В примере выше директива habraHabrNotwork не будет работать корректно, поскольку мы вставляем директиву "{{}}" с переменными в postLink, то есть, когда уже выполнены компиляця и линкование. Иными словами Angularjs даже не знает, что "{{}}" это директива, которая подлежит исполнению.
Другое дело, вторая директива. Там всё на своем месте, мы вставляем шаблон "{{"+attrs.habraHabrNotwork+"+"+attrs.habra+"}}" до компиляции, и он успешно проходит рендеринг.
Остановимся немного на методе compile. Он может возвращать, как функцию postLink, так и объект с двумя параметрами: pre и post. Где pre и post это методы preLink и postLink соответственно. Из названия методов может показаться, что речь идет о методах до и после Linkа. Но это не совсем так, эти функции выполняются до и после Link а детей директивы в DOM. На примере:
[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'; } } } } });
На этом предлагаю сделать паузу. Если тема интересная, в ближайшие дни постараюсь написать продолжение про области видимости и шаблоны.
ссылка на оригинал статьи http://habrahabr.ru/post/179755/
Добавить комментарий