На мой взгляд, директивы являются основной изюминкой декларативного стиля Angularjs. Однако, если открыть комментарии пользователей в разделе официальной документации Angularjs, посвященной директивам, то вы увидите, что самый популярный из них: «Пожалуйста, перепишите документацию, сделайте ее более доступной и структурированной. Начинающему разработчику на Angularjs сложно в ней разобраться» («Please rewrite a clearer well structured documentation of directives., this is not friendly to first time angular developers»). С этим трудно не согласится, документация пока еще сыровата и в некоторых моментах приходится прилагать большие усилия, чтобы разобраться в логике и сути функционала. Поэтому я предлагаю вам свой вольный пересказ данной главы в надежде, что кому-то это позволит сэкономить время, а так же рассчитываю на вашу поддержку и участие в комментариях. Итак, поехали!
Часть 1
- Как писать директивы?
- Простой вариант создания директивы
- Развернутый вариант
- Link и Compile
Template и TemplateUrl
Продолжая разговор о директивах, надо отметить, что директивы по сути являются модулями, если абстрагироваться от терминологии Angularjs. То есть, в идеале, они должны быть самостоятельным элементом интерфейса со своими функционалом и разметкой. Разметка при этом может задаваться напрямую в параметре Template или храниться в отдельном файле, URL которого указывается в TemplateUrl:
[jsFiddle]
angular.module('helloHabrahabr', [])
.directive('habraHabr', function() {
return {
template:"<span>Hello Habr!</span>"
/* или */
templateUrl:"helloHabr.html"
}
});
helloHabr.html
<span>Hello Habr!</span>
При этом в случае, если шаблон подгружается, функции Compile и Link выполняются после загрузки.
Scope
Параметр Scope определяет область видимости внутри директивы. Возможно несколько вариантов:
Не указывать scope вовсе. Тогда директива, грубо говоря, работает напрямую в области видимости контроллера. То есть все переменные контроллера равны переменным директивы.
[jsFiddle]
<div ng-app="helloHabrahabr">
<div ng-controller="forExampleController">
{{hello}}
<span habra-habr></span>
</div>
</div>
function forExampleController($scope){
$scope.hello="Hello Habr!";
}
angular.module('helloHabrahabr', [])
.directive('habraHabr', function() {
return {
template:"<input ng-model='hello'>{{hello}}"
}
});
Другой вариант. Scope = true. В этом случае, scope будет наследоваться. То есть поля, заданные в родительском scope будут отображаться и в scope директивы, но при этом все изменения будут локальны:
[jsFiddle]
function forExampleController($scope){
$scope.hello="Hello Habr!";
}
angular.module('helloHabrahabr', [])
.directive('habraHabr', function() {
return {
template:"<input ng-model='hello'>{{hello}}",
scope:true
}
});
И наконец, самый интересный вариант. Задать изолированный scope. То есть scope, который по умолчанию абсолютно независим от контекста вызова директивы. Для этого нужно просто указать в качестве scope пустой объект {}:
[jsFiddle]
angular.module('helloHabrahabr', [])
.directive('habraHabr', function() {
return {
template:"<input ng-model='hello'>{{hello}}",
scope:{
}
}
});
Дальше есть несколько вариантов работы с таким изолированным scope. Но все они сводятся к одному принципу. В объекте, который мы объявили для scope, в качестве имени свойства слева указывается некая переменная директивы, а справа название атрибута DOM c одним из трех символов в начале: @/=/&. То есть вот так:
scope:{
localVar1:"@attrName1",
localVar2:"=attrName2",
localVar3:"&attrName3"
}
Либо еще одни вариант. Не указывать имя атрибута, тогда оно будет равно имени переменной:
scope:{
localVar1:"@", /*localVar1:"@localVar1" */
localVar2:"=", /*localVar2:"@localVar2" */
localVar3:"&" /*localVar3:"@localVar3" */
}
Теперь по порядку. Префикс "@" означает, что локальной переменной будет присвоено значение атрибута:
[jsFiddle]
<div ng-app="helloHabrahabr">
<span habra-habr="hello" some-attr="Hello Habr!"></span>
</div>
angular.module('helloHabrahabr', [])
.directive('habraHabr', function() {
return {
template:"{{hello}}",
scope:{
hello:'@someAttr'
}
}
});
Префикс "=" означает, что в атрибуте передается уже не строчка, а имя некоторой переменной в текущем Scope. И локальная переменная будет напрямую с ней связана. То есть изменения переменной как внутри директивы, так и вне отразятся и там, и там:
[jsFiddle]
<div ng-app="helloHabrahabr">
<div ng-controller="forExampleController">
{{hello}}
<span habra-habr some-attr="hello"></span>
</div>
</div>
function forExampleController($scope){
$scope.hello="Hello Habr!";
}
angular.module('helloHabrahabr', [])
.directive('habraHabr', function() {
return {
template:"<input ng-model='hello'>{{hello}}",
scope:{
hello:'=someAttr'
}
}
});
И наконец, последний вариант "&" предполагает, что атрибут содержит некое выражение. К примеру, «c= a+b» или проще «a+b». И теперь ваша локальная переменная становится функцией, в которую можно передавать параметры. Параметры передаются в объекте, ключами которого выступают имена переменных в функции. В конкретном случае, localVar({a:1,b:2}) вернет три.
[jsFiddle]
angular.module('helloHabrahabr', [])
.directive('habraHabr', function() {
return {
template:"{{helloFn({a:1,b:2})}}",
scope:{
helloFn:'&someAttr'
}
}
});
При этом интересно, что по умолчанию, если не передавать в локальную функцию никаких параметров, переменным будут присвоены значения соответствующих переменных в родительском scope. А если указать переменную -результат, то и она также будет доступна из вне:
[jsFiddle]
<div ng-app="helloHabrahabr">
<div ng-controller="forExampleController">
a={{a}}
b={{b}}
parent's hello={{hello}}
<span habra-habr some-attr="hello= a+b"></span>
</div>
</div>
function forExampleController($scope){
$scope.a="Hello";
$scope.b=" Habr!";
}
angular.module('helloHabrahabr', [])
.directive('habraHabr', function() {
return {
template:"default helloFn={{helloFn()}}
custom hello={{helloFn({a:'Bye',b:'Habr'})}}",
scope:{
helloFn:'&someAttr'
}
}
});
Всем спасибо, продолжение следует.
Автор: durovchpoknet