AngularJS’Внутренние в глубине, часть 2

В предыдущая статья в этом series1Я обсуждали события и области действия поведение цикла дайджеста.На этот раз, я буду говорить о директивах.Эта статья будет охватывать изолировать областей, включение, связывая функции, компиляторы, контроллеры директивы и more.

Если фигура выглядит необоснованно галлюциногенный, то эта статья может быть для вас .

Angular2
(Кредит изображения: Угловая JS documentation 3 ) ( Большая version 4 )

Отказ от ответственности: Эта статья основана на AngularJS v1.3.0 tree5.

Что, черт возьми, Директива?Link

Директива является как правило, smallкомпонент, который предназначен для взаимодействия с DOM в AngularJS.Он используется в качестве уровня абстракции в верхней части DOM, и наиболее манипуляции может быть достигнуто, не касаясь элементов DOM, завернутые в JQuery, jqLite или иным образом.Это достигается с помощью выражения, и другие директивы, чтобы достичь желаемых результатов .

Директивы в ядре AngularJS “можно привязать свойства элемента ( таких, как видимость, список класса, внутренний текст, HTML, внутренней или value) имущества Область применения или выражения.В частности, эти привязки будут обновлены, когда изменения в сфере перевариваются, используя часы.Точно так же, и в обратном направлении, DOM атрибуты мы можем “смотрел” используя $observeфункция, которая будет вызывать функцию обратного вызова всякий раз, когда смотрел недвижимость меняет .

Директивы, попросту говоря, один из самых важных лицо AngularJS.Если вы владеете директивы, то у вас не будет каких-либо вопросы, связанные с AngularJS приложений.Точно так же, если вы не смогли овладеть директив, вы будете cluelessly хватаюсь за соломинку, не зная, что вы будете рядом снять.Освоение директивы требует времени, особенно еслиВы пытаетесь держаться подальше от просто оборачивать фрагмент JQuery питанием код и называть это день .

В AngularJS, вы сможете построить компонентной директивы, услуги и контроллеры, которые могут быть повторно использованы так часто, как это имеет смысл для них, чтобы использовать повторно.Например, у вас может быть простой директиву, которая превращает по классу на основе выражения в области видимости смотрел, и я бы себе представить, что было бы довольно распространенная директива, используется везде в вашем приложении, чтобы сигнализировать состояние конкретного компонента в вашемкод.Вы могли бы услугу агрегировать сочетанием клавиш обработки, и имеют контроллеры, директивы и другие услуги регистрации ярлыки с этой службы, укоренения все клавиатуре ярлык обработки в одном красиво автономный сервис .

Директивы также многоразовые куски функциональности, но чаще всего, они назначены DOM фрагментов, или templates, А не просто обеспечивает функциональные возможности.Время, чтобы нырнуть глубоко в AngularJS директив и их прецедентами .

Создание Директиву Link

Ранее я перечислены каждое свойство доступной на scope6в AngularJS, и я использовал это, чтобы объяснить механизм дайджест и как объемы работать.Я сделаю то же самое для директивы, но на этот раз я пойду через свойства объекта, возвращаемого функцией директиву в заводской и как каждый из этих свойств влияет на директиву, определяющую мы .

Первое, что следует отметить имя директивы.Давайте посмотрим на краткий пример .

angular.module('PonyDeli').directive('pieceOfFood', function () {
  var definition = { // <- these are the options we'll be discussing
    template: // ...
  };
  return definition;
});

Даже если в фрагменте выше мы определяем директиву по имени 'pieceOfFood', AngularJS Конвенция предусматривает, что мы используем двойную версию этого имени в HTML-разметки.То есть, если эта директива были реализованы в качестве атрибута, то я, возможно, потребуется передать его в моем HTML вот так:

<span piece-of-food></span>

По умолчанию, директивы могут быть вызваны только в качестве атрибутов.Но что, если вы хотите изменить это поведение?Вы можете использовать restrictОпция .

  • restrict7Определяет, как директива может применяться в markup
angular.module('PonyDeli').directive('pieceOfFood', function () {
  return {
    restrict: 'E',
    template: // ...
  };
});

По какой-то причине я не могу понять, они решили, чтобы скрыть то, что в противном случае подробный рамки, и мы закончили с одиночными буквами, чтобы определить, как директива ограничена.Список доступно restrictchoices8появляется на GitHub, иЗначение по умолчанию составляет EA9.

  • 'A': Атрибуты разрешены<span piece-of-food></span>
  • 'E': Элементы allowed
    <piece-of-food></piece-of-food>
  • 'C': В качестве имени класса<span class='piece-of-food'></span>
  • 'M': Как комментарий<!-- directive: piece-of-food -->
  • 'AE': Вы можете комбинировать любые из них, чтобы ослабить ограничение немного .

Никогда не используйте 'C'или 'M'чтобы ограничить директивы.Использование 'C'не выделяются в разметке, и 'M'был предназначен для обеспечения обратной совместимости.Если вы чувствуете, как смешно, хотя, вы могли бы сделать дело для установления restrictдо 'ACME'.

(Помните, как в последней статье я сказал посоветоваться с щепоткой salt? Не сделать с моим —! Мой совет является удивительным)

К сожалению, остальные свойства в объекте определения директивы являются гораздо более неясным .

  • scope10устанавливает, как директива взаимодействует с $parentscope

Потому что мы обсуждали областей в длину и в предыдущей статье, узнать, как использовать scopeнедвижимость properlyне должно быть все, что мучительной.Давайте начнем со значением по умолчанию, scope: false, Где цепь объем остается неизменным: Вы получите все сфера является foundна соответствующем элементе, следуя правилам, я говорилось в предыдущем article11.

Оставив нетронутыми цепи сфера, очевидно, полезно, когда ваш директива не взаимодействует с областью вообще, но редко.Гораздо чаще сценарий, в котором не касаясь сфера является полезным является создание директиву, которая не имеет никаких оснований, чтобы быть инстанс несколько раз на любой сферы и, что просто взаимодействует с одного свойства области видимости, то Директивы name,Это наиболее декларативный, когда в сочетании с restrict: 'A', По умолчанию restrictстоимость.(Код ниже доступно на Codepen __57 | 12 .)

angular.module('PonyDeli').directive('pieceOfFood', function () {
  return {
    template: '{{pieceOfFood}}',
    link: function (scope, element, attrs) {
      attrs.$observe('pieceOfFood', function (value) {
        scope.pieceOfFood = value;
      });
    }
  };
});
<body ng-app='PonyDeli'> 
  <span piece-of-food='Fish & Chips'></span>
</body>

Есть несколько вещей, чтобы отметить здесь, что мы еще не обсуждали.Вы узнаете больше о linkнедвижимость в этой статье.На данный момент, думаю, о нем, как контроллер, который работает для каждого экземпляра directive.

В Директивесвязывая функция, мы можем получить доступ attrs, Которая представляет собой сборник из атрибутов представить на element,Эта коллекция имеет особый метод, называемый $observe(), Который будет стрелять обратного вызова всякий раз, когда это свойство changes13,Без наблюдая за изменениями атрибут, свойство не будет когда-либо сделать его рамки, и мы не смогли бы связываться с ним в нашем шаблоне .

Мы можем крутить выше код, что делает его гораздо более полезным, добавляя scope.$evalк соединению.Помните, как он может быть использован для оценки выражение против рамки?Посмотрите на код ниже ( также на Codepen __10 | 14 ), чтобы получить лучшее представление о том, как, что может помочь нам .

var deli = angular.module('PonyDeli', []);

deli.controller('foodCtrl', function ($scope) {
  $scope.piece = 'Fish & Chips';
});

deli.directive('pieceOfFood', function () {
  return {
    template: '{{pieceOfFood}}',
    link: function (scope, element, attrs) {
      attrs.$observe('pieceOfFood', function (value) {
        scope.pieceOfFood = scope.$eval(value);
      });
    }
  };
});
<body ng-app='PonyDeli' ng-controller='foodCtrl'>
  <span piece-of-food='piece'></span>
</body>

В этом случае, я оценка значение атрибута, pieceПротив объема, который определил $scope.pieceна контроллере.Конечно, вы могли бы использовать шаблон, как {{piece}}непосредственно, но это потребует определенных знаний, о которых собственность в сферу вы хотите отслеживать.Эта модель обеспечивает немного больше flexibility, Хотя вы все еще собираетесь быть разделяя области действия всех directives, Которые могут привести к неожиданно behaviorесли вы попытаетесь добавить больше чем один директиву в той же области .

Игривый ребенок Области Link

Вы могли бы решить эту проблему путем создания детского область, которая наследует прототипически от своего родителя.Для создания детского область, вы просто должны объявить scope: true.

var deli = angular.module('PonyDeli', []);

deli.controller('foodCtrl', function ($scope) {
  $scope.pieces = ['Fish & Chips', 'Potato Salad'];
});

deli.directive('pieceOfFood', function () {
  return {
    template: '{{pieceOfFood}}',
    scope: true,
    link: function (scope, element, attrs) {
      attrs.$observe('pieceOfFood', function (value) {
        scope.pieceOfFood = scope.$eval(value);
      });
    }
  };
});
<body ng-app='PonyDeli' ng-controller='foodCtrl'>
  <p piece-of-food='pieces[0]'></p>
  <p piece-of-food='pieces[1]'></p>
</body>

Как вы можете видеть, мы теперь в состоянии использовать несколько instances15директивы и получить желаемое поведение, потому что каждый директива создает свою область.Тем не менее, есть ограничение: Несколько директивы на элементе все получают одинаковый объем .

Примечание: Если несколько директив на той же просьбой элемента новой области, то только один новый объем создан .

Одинокая, изолировать Scope Link

Одна последняя опция для создания местных, или изолировать, область.Разница между изолята объема и ребенка рамки, что бывший не наследует от своих родителей (но это по-прежнему доступны на scope.$parent ).Вы можете объявить изолят область, как это: scope: {},Можете добавитьсвойства к объекту, которые получают данные с привязкой к родительской области, но доступны на локальной области.Многое, как restrictИзолировать свойства область применения имеют лаконичный, но в заблуждение синтаксис, в котором вы можете использовать символы, такие как &@и =чтобы определить, как свойство связано .

Вы можете опустить имя свойства, если вы собираетесь использовать его в качестве ключа в вашей локальной области.То есть, pieceOfFood: '='является сокращением для pieceOfFood: '=pieceOfFood';они эквивалентны .

Выберите свое оружие: @&Или =

Что эти символы означают, что ли?Примеры, которые я закодированные, перечисленных ниже, может помочь вам расшифровать их .

Атрибут наблюдателя: @

Использование @связывается с результатом наблюдаем attribute16против родительской области .

<body ng-app='PonyDeli' ng-controller='foodCtrl'>
  <p note='You just bought some {{type}}'></p>
</body>
deli.directive('note', function () {
  return {
    template: '{{note}}',
      scope: {
        note: '@'
      }
  };
});

Это эквивалентно наблюдения attribute17изменения и обновления нашей местной сферы.Конечно, с помощью @обозначения гораздо более "AngularJS."

deli.directive('note', function () {
  return {
    template: '{{note}}',
    scope: {},
    link: function (scope, element, attrs) {
      attrs.$observe('note', function (value) {
        scope.note = value;
      });
    }
  };
});

Атрибут наблюдатели являются наиболее полезными, когда потребляя варианты для directive,Если мы хотим, чтобы изменить поведение директива, основанная на изменении параметров, хотя, то написания attrs.$observeлинии сами могли бы сделать больше смысла, чем с AngularJS сделать это internally18и создания смотреть на нашем конце, который будет медленнее .

В этих случаях, простой заменой scope.note = value, В $observeобработчик показано выше, в то, что вы бы поставили на $watchслушатель должен сделать .

Примечание: имейте в виду, что, имея дело с @, Мы говорить о соблюдении и attributeВместо привязки к родительской области .

Построитель выражений: &

Использование &дает вам Выражение-оценки function19в контексте родительской области .

<body ng-app='PonyDeli' ng-controller='foodCtrl'>
  <p note='"You just bought some " + type'></p>
</body>
deli.directive('note', function () {
  return {
    template: '{{note()}}',
    scope: {
      note: '&'
    }
  };
});

Ниже я изложил, как можно реализовать эту же функциональность в функции связывания, в случае, если вы не в курсе &,Это одна чуть более длительная, чем @, Потому что это разбор выражения в attribute20один раз, строительство многоразовый функцию .

deli.directive('note', function ($parse) {
  return {
    template: '{{note()}}',
    scope: {},
    link: function (scope, element, attrs) {
      var parentGet = $parse(attrs.note);

      scope.note = function (locals) {
        return parentGet(scope.$parent, locals);
      };
    }
  };
});

Строители экспрессии, как мы видим, создает метод, который запрашивает родительский область.Вы можете выполнить метод, когда вы хотите и даже смотреть его для вывода изменений.Этот метод следует рассматривать как запрос только для чтения по материнской выражения и, таким образом, будет наиболее полезным в двух сценариях.Первый, когда вам нужно, чтобы следить за изменениями на родительской области, в этом случае вы бы созданы часы на выражение функции note(), Который, в сущности, то, что мы сделали в примере выше .

Другая ситуация, в которой это может пригодиться, когда вам нужен доступ к метода на родительской области.Предположим, что родитель сфера имеет метод, который обновляет таблицу, в то время как ваш локальная область представляет собой строку таблицы.Когда строка таблицы будет удалена, вы можете обновить таблицу.Если кнопка находится в детской рамки, то имело бы смысл использовать &связывания для доступа к функциям обновления на родительской области.Это просто надуманный пример —Вы могли бы предпочесть использовать события для такого рода вещи, или даже структурировать приложение в некотором роде, так что, осложняющие такие вещи можно избежать .

Двунаправленная Переплет: =

Использование =устанавливает двунаправленный binding21между местными и родительских областей .

<body ng-app='PonyDeli' ng-controller='foodCtrl'>
  <button countable='clicks'></button>
  <span>Got {{clicks}} clicks!</span>
</body>
deli.directive('countable', function () {
  return {
    template:
      '<button ng-disabled="!remaining">' +
        'Click me {{remaining}} more times! ({{count}})' +
      '</button>',
    replace: true,
    scope: {
      count: '=countable'
    },
    link: function (scope, element, attrs) {
      scope.remaining = 10;

      element.bind('click', function () {
        scope.remaining--;
        scope.count++;
        scope.$apply();
      });
    }
  };
});

Двунаправленный связывание немного больше complicated22чем &или @.

deli.directive('countable', function ($parse) {
  return {
    template:
      '<button ng-disabled="!remaining">' +
        'Click me {{remaining}} more times! ({{count}})' +
      '</button>',
    replace: true,
    scope: {},
    link: function (scope, element, attrs) {

      // you're definitely better off just using '&'

      var compare;
      var parentGet = $parse(attrs.countable);
      if (parentGet.literal) {
        compare = angular.equals;
      } else {
        compare = function(a,b) { return a === b; };
      }
      var parentSet = parentGet.assign; // or throw
      var lastValue = scope.count = parentGet(scope.$parent);

      scope.$watch(function () {
        var value = parentGet(scope.$parent);
        if (!compare(value, scope.count)) {
          if (!compare(value, lastValue)) {
            scope.count = value;
          } else {
            parentSet(scope.$parent, value = scope.count);
          }
        }
        return lastValue = value;
      }, null, parentGet.literal);

      // I told you!

      scope.remaining = 10;

      element.bind('click', function () {
        scope.remaining--;
        scope.count++;
        scope.$apply();
      });
    }
  };
});

Эта форма связывания данных является Вероятно, наиболее usefulвсего три.В этом случае свойство родительской области хранится в синхронизации с локальной области видимости.Всякий раз, когда обновленное значение локальной области, он получает набор на родительской области.Точно так же, всякий раз, когда изменяется значение родительской области, локальная область обновляется.Самый простой сценарий У меня для вас, когда это было бы полезно это когда у вас есть ребенок область, которая используется для представления суб-модель родительской области.Подумайте о вашем типичном CRUDЕсли вы хотите прочитать полностью статью, посетите сайт наших спонсоров

Comments are closed.