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


529

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

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

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

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

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

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




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

Ответы:


470

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

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

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

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

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

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

Контроллер:

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


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
    }
  }
});

1
Вы упомянули, что показали, как получить parentDirectiveCtrl в дочерний контроллер ... В этом примере у дочернего элемента нет контроллера, а есть функция связи ... В данный момент я не застрял в этом вопросе, так что это может быть такой важный, но любопытный вопрос.
alockwood05

13

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

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

против

link: function(scope, element, attrs) {... //no services allowed

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.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.