Angular JS: Зачем нужна функция связи директивы, когда у нас уже был контроллер директивы с областью действия?

199

Мне нужно выполнить некоторые операции над областью действия и шаблоном. Кажется, что я могу сделать это либо в linkфункции, либо в controllerфункции (поскольку оба имеют доступ к области действия).

В каком случае я должен использовать linkфункцию, а не контроллер?

angular.module('myApp').directive('abc', function($timeout) {
    return {
        restrict: 'EA',
        replace: true,
        transclude: true,
        scope: true,
        link: function(scope, elem, attr) { /* link function */ },
        controller: function($scope, $element) { /* controller function */ }
    };
}

Также я понимаю, что linkэто не угловой мир. Таким образом, я могу использовать $watch, $digestи $apply.

Какое значение имеет linkфункция, когда у нас уже был контроллер?

Югаль Джиндл
источник
9
Что вы имеете в виду « Кроме того , я понимаю , что ссылка является не угловой мир. Таким образом, я могу использовать $watch, $digestи $apply. »?
musical_ut
2
Внутри linkмы не видим никакой угловой магии. т.е. нет двухсторонних привязок и т. д. Просто у нас есть доступный для использования API угловой.
Югаль Джиндл

Ответы:

299

После моей первоначальной борьбы с linkи controllerфункциями и чтения довольно много о них, я думаю , что теперь у меня есть ответ.

Сначала давайте поймем ,

Как угловые директивы работают в двух словах:

  • Мы начинаем с шаблона (в виде строки или загружается в строку)

    var templateString = '<div my-directive>{{5 + 10}}</div>';

  • Теперь это templateStringзавернуто как угловой элемент

    var el = angular.element(templateString);

  • С el, теперь мы скомпилируем его, $compileчтобы вернуть функцию ссылки .

    var l = $compile(el)

    Вот что происходит,

    • $compile проходит через весь шаблон и собирает все директивы, которые он распознает.
    • Все обнаруженные директивы рекурсивно компилируются, а их linkфункции собираются.
    • Затем все linkфункции оборачиваются в новую linkфункцию и возвращаются как l.
  • Наконец, мы предоставляем scopeфункцию этой функции l(link), которая дополнительно выполняет функции обернутой ссылки с этим scopeи их соответствующими элементами.

    l(scope)

  • Это добавляет templateновый узел к DOMи вызывает, controllerкоторый добавляет его часы в область, которая используется совместно с шаблоном в DOM.

введите описание изображения здесь

Сравнение компиляции против линии против контроллера :

  • Каждая директива компилируется только один раз, и функция связи сохраняется для повторного использования. Следовательно, если что-то применимо ко всем экземплярам директивы, ее следует выполнять внутри compileфункции директивы .

  • Теперь после компиляции у нас есть linkфункция, которая выполняется при подключении шаблона к DOM . Поэтому мы выполняем все, что является специфическим для каждого экземпляра директивы. Например: прикрепление событий , изменение шаблона в зависимости от области видимости и т. Д.

  • Наконец, контроллер должен быть доступен для работы и реагирования, в то время как директива работает надDOM (после присоединения). Следовательно:

    (1) После настройки просмотра [ V ] (т.е. шаблон) со ссылкой. $scopeнаш [ M ] и $controllerнаш [ C ] в MVC

    (2) Воспользуйтесь двухсторонним связыванием с $ scope , настроив часы.

    (3) $scopeОжидается, что часы будут добавлены в контроллер, поскольку именно это отслеживает шаблон во время выполнения.

    (4) Наконец, controllerтакже используется для связи между соответствующими директивами. (Как myTabsпример в https://docs.angularjs.org/guide/directive )

    (5) Это правда, что мы могли бы сделать все это и в linkфункции, но это касается разделения интересов .

Поэтому, наконец, у нас есть следующее, которое идеально подходит для всех частей:

введите описание изображения здесь

Югаль Джиндл
источник
5
Я также нашел эту статью полезной для понимания порядка выполнения здесь: подробность функций компиляции и компоновки внутри директив AngularJS
Бобби
4
Отличное объяснение. Хотелось бы отметить, что контроллер вызывается перед функцией ссылки.
jsbisht
38
контроллер исполняется до ссылки
Рой Намир
10
Меня бесит, что переполнение стека требует, чтобы в редактировании было не менее 6 символов, что не позволяет мне исправлять написание в этом ответе.
user1886323
79

Зачем нужны контроллеры

Разница между linkиcontroller вступает в игру, когда вы хотите вложить директивы в DOM и представить функции API из родительской директивы для вложенных.

Из документов :

Рекомендация: используйте контроллер, если вы хотите представить API другим директивам. В противном случае используйте ссылку.

Допустим , вы хотите иметь две директивы my-formи , my-text-inputи вы хотите my-text-inputдирективы появляться только внутриmy-form и больше нигде.

В этом случае, вы будете говорить при определении директивы , my-text-inputчто она требует контроллера от parentэлемента DOM с использованием требует аргумента, например: require: '^myForm'. Теперь контроллер из родительского элемента будет injectedв linkфункции в качестве четвертого аргумента, следующего $scope, element, attributes. Вы можете вызывать функции на этом контроллере и взаимодействовать с родительской директивой.

Более того, если такой контроллер не найден, возникнет ошибка.

Зачем вообще использовать ссылку

Нет реальной необходимости использовать linkфункцию, если вы определяете, controllerпоскольку $scopeона доступна в controller. Более того, при определении обоих linkи controllerнужно соблюдать осторожность в отношении порядка вызова двух ( controllerвыполняется раньше).

Однако, в соответствии с Angular-способом , большинство DOM-манипуляций и двухстороннего связывания $watchersобычно выполняются в linkфункции, а API для дочерних и $scopeманипуляций - в controller. Это не сложное и быстрое правило, но это сделает код более модульным и поможет разделить задачи (контроллер будет поддерживать directiveсостояние, а linkфункция будет поддерживать DOMвнешние + привязки).

musically_ut
источник
Замечательно. Теперь, вы можете помочь мне со второй частью вопроса?
Югаль Джиндл
Я имею в виду, поскольку у нас был контроллер, который можно было использовать для связи с другими директивами. Итак, в чем была необходимость link?
Югаль Джиндл
1
Ваш ответ почему-то не отвечает на настоящий вопрос.
Югаль Джиндл
1
Есть ли проблемы, которые возникают, когда мы определяем controller? Почему я хочу изобрести совершенно новую функцию, чтобы избежать определения контроллера?
Югаль Джиндл
1
кажется, что @scalaGirl s Link больше не работает
Минато
17

controllerФункция / объект представляет собой абстракцию модель-представление-контроллер (MVC). Хотя нет ничего нового, что можно написать о MVC, это все же самое значительное преимущество angular: разделить проблемы на более мелкие части. И это все, не более того, так что если вам нужно реагировать на Modelизменения, поступающие от человека, Viewто Controllerэто тот человек, который должен выполнять эту работу.

История о linkфункции отличается, она идет с другой точки зрения, чем MVC. И это действительно важно, когда мы хотим пересечь границы controller/model/view (шаблона) .

Давайте начнем с параметров, которые передаются в linkфункцию:

function link(scope, element, attrs) {
  • область действия - это угловой объект области видимости.
  • element - это элемент jqLite-wrapped, которому соответствует эта директива.
  • attrs - это объект с нормализованными именами атрибутов и их соответствующими значениями.

Чтобы поместить linkв контекст, мы должны упомянуть, что все директивы проходят через эти шаги процесса инициализации: Compile , Link . Выдержка из книги Брэда Грина и Шьяма Сешадри Angular JS :

Фаза компиляции (родственная ссылка, давайте упомянем ее здесь, чтобы получить ясную картину):

На этом этапе Angular обходит DOM, чтобы определить все зарегистрированные директивы в шаблоне. Затем для каждой директивы она преобразует DOM на основе правил директивы (template, replace, transclude и т. Д.) И вызывает функцию compile, если она существует. Результатом является скомпилированная функция шаблона,

Фаза связи :

Чтобы сделать представление динамическим, Angular затем запускает функцию связи для каждой директивы. Функции связи обычно создают прослушиватели в DOM или модели. Эти слушатели постоянно синхронизируют вид и модель.

Хороший пример того, как использовать link можно найти здесь: Создание пользовательских директив . См. Пример: создание директивы, управляющей DOM , которая вставляет «дату-время» в страницу, обновляемую каждую секунду.

Просто очень короткий фрагмент из этого богатого источника выше, демонстрирующий реальные манипуляции с DOM. Функция $ timeout подключена, а также очищается при вызове деструктора, чтобы избежать утечек памяти

.directive('myCurrentTime', function($timeout, dateFilter) {

 function link(scope, element, attrs) {

 ...

 // the not MVC job must be done
 function updateTime() {
   element.text(dateFilter(new Date(), format)); // here we are manipulating the DOM
 }

 function scheduleUpdate() {
   // save the timeoutId for canceling
   timeoutId = $timeout(function() {
     updateTime(); // update DOM
     scheduleUpdate(); // schedule the next update
   }, 1000);
 }

 element.on('$destroy', function() {
   $timeout.cancel(timeoutId);
 });

 ...
Радим Келер
источник
3
Вы, кажется, сравнили compilerи link. Они спрашивают, почему, linkкогда у нас это уже былоcontroller
Югал Джиндл
Я расширил ответ, чтобы описать даже контроллер более подробно. Теперь концепции controllerпротив linkдолжны быть более ясными ...
Радим Келер
1
Я могу попытаться довольствоваться этим объяснением. Но там вроде размыто. Было бы здорово, если бы кто-то из угловой команды сам высказался за это, спроецировав, куда, по его мнению, он идет - в linkили в controller.
Югаль Джиндл
1
Это единственная часть, которую я хочу понять (когда этого недостаточно?). Кроме того, я получаю все преимущества от угловатости controllerи linkотносительно некрасиво. Итак, у угловой команды должна быть веская причина, а не просто вариант.
Югаль Джиндл
1
Вопрос: когда контроллера недостаточно? Ответ: Если вам нужен опыт работы вне Angular, например, для использования плагина JQuery или использования функции JQlite, как упомянуто в документе ( docs.angularjs.org/api/ng/function/angular.element:) , тогда вам потребуется ссылка
Hasteq