Pull to refresh

Директивы в Angularjs для начинающих. Часть 2

Reading time 4 min
Views 117K
На мой взгляд, директивы являются основной изюминкой декларативного стиля 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'
            }
        }
    });



Всем спасибо, продолжение следует.
Tags:
Hubs:
+33
Comments 21
Comments Comments 21

Articles