Персональный блог Игоря Антонова aka "spider_net"

Создание директив в angular.js


Рубрика: Программирование -> Angular.JS -> JavaScript
Метки: | | | |
Просмотров: 12050
Создание директив в angular.js

Продолжаю на работе терзать и наслаждаться возможностями angular.js. Сегодня мне потребовалось подключить к своему проекту плагин DatePicker из пакета jQuery UI. Заповеди angular.js диктуют использовать в представлении директивы, а не хаотичной раскиданный JavaScript код. Следовательно, вот так просто воспользоваться возможностями плагина DatePicker не получится. Точней получится, но делать это ни в коем случае нельзя, ибо это идет в разрез с идеологией angular.js. К счастью, пойти правильным путем ничуть не сложней. Достаточно написать собственную директиву и использовать в разных участках приложения. Под катом я разберу процесс написания директивы для angular.js максимально подробно.

За основу возьмем заготовку проекта – «angular-seed». Склонируй ее из GIT и приготовься вносить изменения. Поскольку конечной целью будет подключение компонента DatePricker, нам потребуется добавить библиотеку jQuery UI к проекту. Загружаем все необходимое с официального сайта библиотеки (или ставим через bower), распихиваем в соответствующие директории проекта и подключаем все это добро в «index.html»:

<link rel="stylesheet" href="css/jquery-ui-1.9.2.custom.css" />
<script src="js/jquery-1.8.2.js"></script>
<script src="js/jquery-ui.js"></script>

Создаем директиву

Код директивы будем писать в файле «directives.js». Открываем файл и пишем в него код:

directive('myDatepicker', function() {
   return {
        require: 'ngModel',                        
        link: function (scope, element, attrs, ngModelCtrl) {                                                                                                                  
        $(function() {                                                                                                  
            element.datepicker({
            showOn: 'both',
            changeYear: true,
            changeMonth: true,
            dateFormat: 'dd.mm.yy',
            maxDate: '+1Y',
            yearRange: '1920:2015',
            onSelect:function (dateText, inst) {
              ngModelCtrl.$setViewValue(dateText);
              scope.$apply();
            }
         });
     });                                          
  }
 }
});

Если говорить кратко, то в этой директиве происходит подключение плагина DatePicker. Правда, меня, когда я впервые увидел подобную конструкцию, краткий ответ не устроил. Да, мы видим, что большую часть этого листинга занимает конструктор DatePicker, но почему он записывается именно в функции link, с кучей параметров? Об этом расскажу ниже.

Зачем нужны директивы в angular.js

Как ты уже знаешь, директивы необходимы для расширения функционала и поведения существующих html-элементов. Например, мы хотим, чтобы список у элемента управления «select» выпадал вверх, а не вниз. Мы можем это сделать средствами jQuery прямо в коде, но это будет НЕ angular-way. Правильней оформить этот код в виде универсальной директивы, и в дальнейшем использовать ее там, где это необходимо. Директивы – это шаг в сторону модульности и повторного использования кода.

Если ты сомневаешься в целесообразности создания директивы в определенных случаях, то задай себе пару контрольных вопросов:

  • Требуется ли мне из контроллеров/сервисов взаимодействовать с DOM?
  • Требуется ли мне писать JS-код в представлениях?
  • Потребуется ли мне подключение jQuery кода в разных участках приложения?
  • Наличие положительного ответа на один из перечисленных выше вопросов - гарантия плохой архитектуры и отсутствие понимания идеологии angular. Такие вещи нужно сразу присекать.

    Ок, возвращаемся к нашему коду. Чтобы начать использовать директиву ее необходимо зарегистрировать. Регистрация новой директивы выполняется с помощью метода directive(). Он принимает два параметра:

    - name (string, object) – название директивы в верблюжьим (camel-case) стиле. Применительно к нашему коду это «myDatepicker».

    - directiveFactory (function(), array) - функция-фабрика (factory function). Эта функция должна вернуть объект с настройками для сервиса $compile, отвечающий за компиляцию HTML/DOM в шаблон. Впоследствии он создаст функцию, связывающую skope и наш шаблон.

    Теперь посмотрим на настройки объекта, который будет возвращать наша функция-фабрика. Полный список возможных настроек можно посмотреть в официальной документации. В своем примере я использую лишь:

  • require – директивы, которые будут внедряться в контроллер через четвертый параметр связующей (link) функции. В require можно указать как одну директиву, так и массив. В своем примере я подключаю лишь «ngModel». Она потребуется для организации двухстороннего биндинга.
  • link – в свойстве описывается связующая функция (link function). Определение функции выглядит так:
  • function link(scope, iElement, iAttrs, controller, transcludeFn){}

    В качестве параметров применяются:

    - scope – область видимости. Будет использоваться директивой для регистрации обработчиков событий (в терминологии angular – watches);

    - iElement – экземпляр элемента, в котором будет использоваться директива;

    - iAttrs – экземпляр атрибутов элемента;

    - ctrl – экземпляр контроллера. Необходимо указывать, если одна из директив элемента, определяет контроллер. В нашем случае в роли контроллера будет выступать ngModelController.

    - transcludeFn – признак трансклюзии. Об этом параметре я расскажу в отдельном посте.

    В рассматриваемом примере я опустил пятый вариант и сократил определении link-функции до:

    function (scope, element, attrs, ngModelCtrl)

    В теле функции описывается стандартный конструктор элемента DatePicker. Параметры рассматривать не буду, т.к. отношения к angular они не имеют. Затрону лишь определение обработчика onSelect (). Он должен срабатывать во время выбора в календаре какой-нибудь даты и передавать результат пользовательского выбора в scope.

    В теле метода вызывается метод $setViewValue, обновляющий. В качестве параметра метод принимает значение, полученное из представления. В моем случае – это дата, выбранная в DatePicker. После изменения значения нам необходимо оповестить об этом $scope с помощью метода $apply().

    Тестируем директиву

    Директива полностью готова к применению. Подключение к произвольному элементу ввода выполняется следующим образом:

    <input ng-model="myDateOfBirth" my-datepicker />

    Если никаких ошибок допущено не было, то ты увидишь возле элемента ввода кнопку для вызова календаря. Аналогичное действие будет выполняться и при клике мышкой по элементу ввода.

    На этой самой ноте мой рассказ заканчивается. Если у тебя есть вопросы – задавай их мне в комментариях. Постараюсь помочь.

    Комментариев: 5 RSS

    Как-то сложно выглядит все в вашем angular'е. Мне проще на jquery делать динамику. Я пробовал делать директиву, но забросил, не хрена не работало. Попробую по твоей заметке.

    К нему нужно просто привыкнуть. После jquery все выглядит сверх сложно, но чтение документации и практика побеждает любые трудности.

    Поддерживаю Andry. После jquery понимаешь, что в angular'е вообще ни фига не понятно. Такое ощущение, что angular придумали какие-то внеземные цивилизации. =)

    2Роман,

    Да, на первых порах мозг выносит :-) Дело привычки.

    Кстати, knockout в этом плане намного легче. Если не получается с angular, то можно посмотреть на knockout =)

    Оставьте комментарий!
    comments powered by HyperComments