В первой части мы разобрали как развернуть проект, его структуру, MVC компоненты и как это все тестировать с помощью testacular.
В этой части мы уделим больше внимания шаблонам и директивам.
Итак, что из себя представляют шаблоны AngularJs? Это HTML дополненный переменными, выражениями и директивами.
Переменные
Определяются через двойные фигурные скобки в тексте HTML:
<span>Hello, {{name}}!</span>
либо без кавычек, если указываются в директиве:
<input type="submit" ng-disabled="validate">
Выражения(expression)
Уже разбиралось в первой части, интеграция такая же как и у переменных:
<p>1 + 2 = {{ 1 + 2 }}</p>
В выражении мы так же можем вызвать методы из $scope:
<p>1 + 2 = {{displaySum(1,2)}}</p>
Важно помнить, выражение – это еще не полный JavaScript, поэтому объекты типа Math работать не будут, если конечно мы их предварительно не свяжем через контроллер.
Директивы
Вот тут начинается самое интересное.
Директива – это что-то типа вспомогательного элемента для отрисовки представления. Может быть встроена в HTML следующими способами:
тип | общий вид | пример |
---|---|---|
В виде атрибута тега | <div directive=”expression”> | <div ng-click=”sayHello()”> |
Значением атрибута class | <div class=”directive:expression”> | <div class=”ng-controller:mainCntl”> |
Отдельным тегом | <tag></tag> | <notifcation></notification> |
комметарием | <!– directive: my-dir exp –> | <–! directive: alert–> |
Все директивы можно условно разделить на 2 типа: встроенные(которые уже присутсвуют во фремворке) и кастомные(написаные нами)
Встроенные директивы начинаются с префикса ng- либо ng: (разработчки фреймворка допускают использование обоих вариантов)
Основные встроенные директивы:
Определяющие назначение блока:
- ngApp – определяет корневой элемент для приложения(обычно это html либо body, но могут быть и другие вариаты)
- ngController – задает имя контроллера(метод) для блока
- ngForm – связывает элементы формы
- ngClassEven/ngClassOdd – когда мы хотим что-то сделать для четных/нечетных элементов в списке( используется совместно с ngRepeat)
- ngHide/ngShow – прячет либо показывает элемент в зависимости от значения
- ngInclude – позволяет подключать отдельные файлы
- ngTransclude – используется внутри кастомных директив для вывода контента заданного снаружи при объявлении директивы
- ngRepeat – что-то типа встроенного цикла
- ngModel – связывание состояния/значения элемента с моделью/объектом
- ngChange
- ngClick
- ngDblclick
- ngMousedown/ngMouseenter/ngMouseleave/ngMousemove/ngMouseover/ngMouseup
- ngSubmit
Если же наши потребности не удовлетворяют встроенные директивы – тогда мы можем написать свою. Как это делается? Все директивы создаются с помощью метода модуля – directive(), который имеет следующий синтаксис:
somemodule.directive( directiveName, directiveFactory )
- directiveName – имя нашей директивы
- directiveFactory – фабрика, для создания директивы
Фабрика может возвращать как функцию(простая нотация), так и объект с методами и свойствами(сложная нотация).
Синтаксис простой нотации:
function directiveFactory(){ // directive private methods here return function(scope, element, attrs){ //directive logic } }
Синтакс сложной нотации:
function directiveFactory(){ var directiveDefinitionObject = { priority: 0, template: '', templateUrl: 'directive.html', replace: false, transclude: false, restrict: 'A', scope: false, controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, compile: function compile(tElement, tAttrs, transclude) { return { pre: function preLink(scope, iElement, iAttrs, controller) { ... }, post: function postLink(scope, iElement, iAttrs, controller) { ... } } }, link: function postLink(scope, iElement, iAttrs) { ... } }; return directiveDefinitionObject; }
Подробно о каждом параметре
Параметр | Описание | Пример |
---|---|---|
priority | приоретива выполения(для случая когда на одном элементе несколько директив) | 1 |
template | шаблон HTML | <div ng-click=”doSomething()”></div> |
templateUrl | ссылка на файл шаблона | ./views/alert.html |
replace | если true – то шаблон директивы заменит элемент, false – произойдет append | true |
transclude | компилирует контект элемента и делает возможные его вставку внутрь шаблона (по средством ngTransclude ) | false |
restrict | задает способ встраивания(смотри выше):
|
E |
scope | Определяет способ передачи scope в директиву | true |
controller | метод(либо ссылка на метод) где описано поведение(логика) | dialogDirectiveController |
compile | метод(либо ссылка на метод) с инструкциями по компиляции шаблона | function(tElement, tAttrs, transclude){ … } |
link | основной параметр фабрики – метод(либо ссылка на метод) по связыванию директивы с приложением | function(scope, iElement, iAttrs){ … } |
Опции compile и link
Функция компиляции compile (используется довольно редко) трансформирует HTML шаблон. После чего фукнция линковки link регистрирует обработчики событий на DOM обновленного HTML.
Опция controller
Необходима в случае, когда логика директивы выходит за пределы одного метода и нам уже необходимма группа методов. Такую группу методов мы можем объединить в функцию-контроллер. Это будет специальный тип контроллера “связанный с директивой”, которые должен взаимодействать только с данной директивой.
Опция scope
В зависимости от типа переданного параметра ведет себя по разному:
- (boolean)true – создается новый scope конкретно для этой директивы
- (object hash){} – задается конкретный изолированный scope – т.е. scope не унаследованный от родительского
По умолчанию эта опция выставлена в false (используется scope ближайшего контроллера).
Изолированный scope может влючать в себе ссылки на элементы родительского scope при использовании нетривиального синтаксиса(специальный префикс символ (@, =, & )перед имеем метода/переменой):
- @ – переменную локального scope со значением DOM аттрибута
- = – двустороннее связывание значения атрибута и переменной
- & – позволяет выполнять выражения из аттрибута в рамках родительского scope
В примере с официального сайта мы видим задания scope для директивы следующим образом:
scope: { title:'@zippyTitle' }
Т.е., будет создан изолированый scope со ссылкой на title, которая будет связана с атрибутом zippy-title (Внимание! именно такая нотация для связывания с атрибутом, не zippyTitle, а zippy-title).
Попробуем его максимально упростить…
Я надеюсь вот этот пример (оставлена только суть) позволит разобраться в вопросе связывания переменных изолированного scope с родительским.
Также прошу обратить внимание на проблему которая возникает: при попытке изменения модели дочернего scope – мы теряем связь с родительской.
Думаю будет еще как минимум один пост на структуру scope в Angular, так что для это статьи пока все.