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

529

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

В документах они объясняют, что:

  • Функция компиляции и ссылки используется в разных фазах углового цикла
  • контроллеры распределяются между директивами

Однако для меня не ясно, какой код должен идти куда.

Например: я могу создать функции в компиляции и прикрепить их к области в ссылке или только прикрепить функции к области в контроллере?

Как контроллеры распределяются между директивами, если каждая директива может иметь свой собственный контроллер? Контроллеры действительно общие или это просто свойства области видимости?

schacki
источник
11
См. Также stackoverflow.com/questions/12546945/…
Марк Райкок,
1
Я написал пост с диаграммой жизненного цикла директивы (фаза создания). Может быть, это кому-то поможет: filimanjaro.com/2014/…
среднем Джо

Ответы:

470

Компилировать:

На этом этапе Angular фактически компилирует вашу директиву. Эта функция компиляции вызывается только один раз для каждой ссылки на данную директиву. Например, скажем, вы используете директиву ng-repeat. ng-repeat придется искать элемент, к которому он прикреплен, извлекать фрагмент html, к которому он прикреплен, и создавать функцию шаблона.

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

Фаза компиляции - это тот шаг в Angular, который возвращает функцию шаблона. Эта шаблонная функция в угловой называется связующей функцией.

Фаза связывания:

На этапе связывания вы присоединяете данные ($ scope) к функции связывания, и она должна вернуть вам связанный HTML. Так как директива также указывает, куда этот html идет или что он меняет, это уже хорошо. Это функция, в которой вы хотите внести изменения в связанный HTML, то есть в HTML, к которому уже прикреплены данные. В угловом, если вы пишете код в функции связывания, это обычно функция post-link (по умолчанию). Это своего рода обратный вызов, который вызывается после того, как функция связывания связала данные с шаблоном.

Контроллер:

Контроллер - это место, где вы вводите некоторую директиву определенной логики. Эта логика также может входить в функцию связывания, но тогда вам придется поместить эту логику в область действия, чтобы сделать ее «разделяемой». Проблема в том, что вы тогда испортили бы область действия своими директивами, что на самом деле не то, что ожидается. Так какой же вариант, если две директивы хотят общаться друг с другом / сотрудничать друг с другом? Конечно, вы можете поместить всю эту логику в службу, а затем сделать обе эти директивы зависимыми от этой службы, но это просто добавляет еще одну зависимость. Альтернативой является предоставление Контроллера для этой области (обычно изолировать область?), А затем этот контроллер вводится в другую директиву, когда эта директива «требует» другую.

ganaraj
источник
67
Чтобы уточнить: компилирует компилирует шаблон, который будет использоваться по всей странице. Линкер привязан к каждому экземпляру. Правильно? Контроллер затем работает между экземплярами.
Златко
4
@CMCDragonkai для каждой директивной controllerфункции выполняется после компиляции, но до этого pre-link в локальной ветви дерева DOM. Также controllerи pre-linkфункции выполняются через локальную ветвь DOM сверху вниз . После этого post-linkвыполняется снизу вверх .
Артем Платонов
9
Это только беспорядок, если ты этого не понимаешь. Для этого есть причина делать то, что он делает.
demisx
3
Это правильный технический ответ, однако у меня все еще остаются вопросы о том, когда мне следует использовать функцию ссылки.
Николас Маршалл
2
Должны ли мы использовать controllerвместо linkвезде? Так что мне не нужно менять код в будущем, если метод должен использоваться совместно или вводиться некоторая логика? Есть ли какие-либо подводные камни в использовании controllerвсе время вместо ссылки?
JPS
99

Я хотел бы добавить также то, что говорится в книге O'Reily AngularJS от команды Google:

Контроллер - Создайте контроллер, который публикует API для взаимодействия между директивами. Хорошим примером является Директива по Директиве Коммуникации

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

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

Николас Динан
источник
Ваша ссылка thinkster.io не может быть просмотрена без оплаты. Не моя ссылка, но, возможно, это больше подходит: toddmotto.com/directive-to-directive-communication-with-require
Р. ван Твиск
51

A directiveпозволяет расширять словарный запас HTML декларативным способом для создания веб-компонентов. ng-appАтрибут является директивой, так ng-controllerи все ng- prefixed attributes. Директивы могут быть attributes, tagsили даже class names, comments.

Как рождаются директивы ( compilationи instantiation)

Компиляция: мы будем использовать compileфункцию как manipulateдля DOM до ее рендеринга, так и для возврата linkфункции (которая будет обрабатывать ссылки для нас). Это также место для размещения любых методов, которыми нужно поделиться со всей instancesэтой директивой.

ссылка: Мы будем использовать linkфункцию для регистрации всех слушателей на конкретном элементе DOM (который клонирован из шаблона) и настроим наши привязки к странице.

Если установлено в compile()функции, они были бы установлены только один раз (что часто является тем, что вы хотите). Если установлено в link()функции, они будут установлены каждый раз, когда элемент HTML привязан к данным в объекте.

<div ng-repeat="i in [0,1,2]">
    <simple>
        <div>Inner content</div>
    </simple>
</div>

app.directive("simple", function(){
   return {
     restrict: "EA",
     transclude:true,
     template:"<div>{{label}}<div ng-transclude></div></div>",        
     compile: function(element, attributes){  
     return {
             pre: function(scope, element, attributes, controller, transcludeFn){

             },
             post: function(scope, element, attributes, controller, transcludeFn){

             }
         }
     },
     controller: function($scope){

     }
   };
});

CompileФункция возвращает функцию preи postссылку. В функции предварительной ссылки у нас есть шаблон экземпляра, а также область действия изcontroller , но шаблон не привязан к области действия и по-прежнему не имеет включенного содержимого.

PostФункция link - это то, где post link является последней функцией, которая будет выполнена. Теперь transclusionзавершено the template is linked to a scope, и view will update with data bound values after the next digest cycle. Эта linkопция является просто ярлыком для настройки post-linkфункции.

контроллер: контроллер директивы может быть передан в другую фазу соединения / компиляции директивы. Он может быть введен в другие направления в качестве средства для использования в межправительственной связи.

Вы должны указать имя требуемой директивы - она ​​должна быть связана с тем же элементом или его родителем. Имя может начинаться с префикса:

?  Will not raise any error if a mentioned directive does not exist.
^  Will look for the directive on parent elements, if not available on the same element.

Используйте квадратную скобку, [‘directive1′, ‘directive2′, ‘directive3′]чтобы требовать нескольких директив контроллера.

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

app.controller('MainCtrl', function($scope, $element) {
});

app.directive('parentDirective', function() {
  return {
    restrict: 'E',
    template: '<child-directive></child-directive>',
    controller: function($scope, $element){
      this.variable = "Hi Vinothbabu"
    }
  }
});

app.directive('childDirective', function() {
  return {
    restrict:  'E',
    template: '<h1>I am child</h1>',
    replace: true,
    require: '^parentDirective',
    link: function($scope, $element, attr, parentDirectCtrl){
      //you now have access to parentDirectCtrl.variable
    }
  }
});
Thalaivar
источник
1
Вы упомянули, что показали, как получить parentDirectiveCtrl в дочерний контроллер ... В этом примере у дочернего элемента нет контроллера, а есть функция связи ... В данный момент я не застрял в этом вопросе, так что это может быть такой важный, но любопытный вопрос.
alockwood05
13

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

controller: function($scope, $exceptionHandler, $attr, $element, $parse, $myOtherService, someCrazyDependency) {...

против

link: function(scope, element, attrs) {... //no services allowed
ScaryBunny
источник
2
Пожалуйста, оставьте комментарий, чтобы объяснить свою точку зрения, когда вы отрицаете ответ. Спасибо
svassr
53
Я не был downvoter, но это не совсем правильно , потому что вы все еще можете придать любую требуемую зависимость в самой директивы, например: module.directive('myDirective', function($window) { etc.... Затем можно получить доступ из функции ссылки.
Майк Чемберлен
1
это кажется некорректным, поскольку вы можете добавить сервисы в функцию ссылки
Code Whisperer
1
@JoshRibakoff Конечный результат тот же, у вас есть доступ к сервису в функции ссылки. Не имеет значения, объявлено ли оно в аргументах функции или нет. В связи с этим Майк Чемберлен правильно
Connor Wyatt
1
@ cwyatt1 Я исправлял язык, plnkr не показывает инъекцию в функцию link (), потому что это не функция Angular. Вы можете подумать, что я педантичен, но в комментариях метаматов уже изложены многочисленные важные различия между тем, что делает этот plunkr, и тем, что делает инъекция в контроллер. ОП спрашивает, в чем различия, и есть различия.
Джош Рибаков
10

это хороший пример для понимания этапов директивы http://codepen.io/anon/pen/oXMdBQ?editors=101

var app = angular.module('myapp', [])

app.directive('slngStylePrelink', function() {
    return {
        scope: {
            drctvName: '@'
        },
        controller: function($scope) {
            console.log('controller for ', $scope.drctvName);
        },
        compile: function(element, attr) {
            console.log("compile for ", attr.name)
            return {
                post: function($scope, element, attr) {
                    console.log('post link for ', attr.name)
                },
                pre: function($scope, element, attr) {
                    $scope.element = element;
                    console.log('pre link for ', attr.name)
                        // from angular.js 1.4.1
                    function ngStyleWatchAction(newStyles, oldStyles) {
                        if (oldStyles && (newStyles !== oldStyles)) {
                            forEach(oldStyles, function(val, style) {
                                element.css(style, '');
                            });
                        }
                        if (newStyles) element.css(newStyles);
                    }

                    $scope.$watch(attr.slngStylePrelink, ngStyleWatchAction, true);

                    // Run immediately, because the watcher's first run is async
                    ngStyleWatchAction($scope.$eval(attr.slngStylePrelink));
                }
            };
        }
    };
});

HTML

<body ng-app="myapp">
    <div slng-style-prelink="{height:'500px'}" drctv-name='parent' style="border:1px solid" name="parent">
        <div slng-style-prelink="{height:'50%'}" drctv-name='child' style="border:1px solid red" name='child'>
        </div>
    </div>
</body>
Амин Рахими
источник
4
Не могли бы вы пояснить, почему этот пример кода поможет понять разницу между link, compileи controller?
Cel
Вы знаете, как requireдиректива d может быть введена в контроллер зависимой директивы?
alockwood05
Вы пишете пример: Uncaught Ошибка: [$ injector: modulerr] Не удалось создать экземпляр модуля myapp из-за: Ошибка: [$ injector: unpr] Неизвестный поставщик: slngStylePrelinkProvider
rofrol
7
  • compile : используется, когда нам нужно изменить шаблон директивы, например добавить новое выражение, добавить другую директиву внутри этой директивы
  • контроллер : используется, когда нам нужно поделиться / повторно использовать данные $ scope
  • ссылка : это функция, которая используется, когда нам нужно присоединить обработчик событий или манипулировать DOM.
HamidKhan
источник