ОБНОВИТЬ
(внизу я добавил код/plnkr, показывающий подход)
Помимо упомянутой вами статьи: https://www.airpair.com/angularjs/posts/preparing-for-the-future-of-angularjs#3-3-match-controllers-with-directives, который в основном не только защищает шаблон, который вы запрашиваете, но и внешний интерфейс на основе компонентов в целом, я нашел: http://joelhooks.com/blog/2014/02/11/lets-make-full-ass-angularjs-directives/ (он поддерживает Минимальное использование функции ссылки и используйте ui-bootstrap в качестве примера, где использовался такой шаблон). Я не могу не согласиться с обеими этими статьями.
Еще одна вещь об Angular2.0: больше нет $scope
в angular2.0 -- https://www.youtube.com/watch?v=gNmWybAyBHI&t=12m14s, поэтому, если вы сможете максимально избавиться от $scope
, то переход будет более плавным.
Я тоже сделал небольшую ошибку:
Тем не менее, я предпочитаю определять все функции в controller
и просто вызывать их через область видимости link
. В идеале это всего один вызов: scope.init
ctrl.init(/*args*/)
(где ctrl — контроллер директивы).
В какой-то степени это дело вкуса, но есть веские причины сделать функцию link
как можно более тонкой:
Логику в функции ссылки нелегко проверить. Конечно, вы можете скомпилировать директиву в своих модульных тестах и протестировать ее поведение, но сама функция ссылки — это черный ящик.
Если вам нужно использовать controller
(скажем, для связи между директивами), то у вас будет два места для размещения вашего кода. Это сбивает с толку, но если вы решили сделать функцию link
тонкой, то все, что можно поместить в controller
, нужно поместить в controller
.
Вы не можете внедрить дополнительные зависимости непосредственно в функцию link
(вы все равно можете использовать те, которые внедрены в функцию main). В случае подхода controller
такой проблемы нет. Почему это важно:
- it keeps better structure of the code, by having the dependencies closer to the context where they are needed
- люди, приходящие в angular с фоном, отличным от JS, все еще имеют проблемы с тем, как функциональное закрытие работает в JS
Итак, что нужно поместить в функцию ссылки:
- Все, что нужно запустить после вставки элемента в DOM. Если
$element
выставлено $on('linked')
событие, то в основном этот пункт недействителен.
- Получение ссылок на контроллеры
require:
ed. Опять же, если бы можно было ввести их напрямую в controller
...
Тем не менее, я предпочитаю определять все функции в controller
и просто вызывать их через область видимости link
. В идеале это всего один вызов: scope.init
.
Миско Хевери пару раз сказал, что DDO далека от совершенства и простоты для понимания, и что она эволюционировала до того, что есть сейчас. Я почти уверен, что если бы дизайнерские решения были приняты заранее, то было бы единственное место для размещения логики директивы - как это будет в angular2.0.
Теперь, отвечая на ваш вопрос, следует ли вам преобразовать функцию link
в функцию controller
. Это действительно зависит от ряда критериев, но если код активно развивается, то, наверное, стоит задуматься. Мой опыт (и несколько человек, о которых я говорил) можно проиллюстрировать этим изображением:
Что касается angular2.0 - это будет тектонический сдвиг, поэтому с этой точки зрения это не должно иметь большого значения, но все же подход controller
кажется более близким к тому, как директивы/компоненты будут объявляться в версии 2.0 через Классы ES6.
И последнее: в какой-то степени это дело вкуса, но есть несколько веских причин, чтобы функция CONTROLLER
оставалась тонкой (путем делегирования логики службам).
ОБНОВЛЕНИЕ -- PLNKR
PLNKR, иллюстрирующий подход:
html
<input ng-model="data.name"/>
<top-directive>
<my-directive my-config="data">
</my-directive>
</top-directive>
js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.data = { name : 'Hello, World'};
});
app.controller('MyCtrl', function($scope){
var self = this;
this.init = function(top){
this.topCtrl = top;
this.getTopName = top.getName.bind(top);
this.getConfigName = function(){return this.config.name};
console.log('initilizing', this, $scope, this.getConfigName, this.getTopName());
}
// if you want to $watch you have to inject $scope
// you have access to the controller via name defined
// in contollerAs
$scope.$watch('myCtrl.config', function(){
console.log('config changed', self.getConfigName());
}, true);
});
app.directive('topDirective', function(){
return {
controller : function(){
this.name = "Hello, Top World";
this.getName = function(){return this.name};
}
}
});
app.directive('myDirective', function(){
return {
require: ['myDirective', '^topDirective'],
controller : 'MyCtrl',
bindToController: true,
controllerAs: 'myCtrl',
template : '{{myCtrl.getConfigName() + " --- " + myCtrl.getTopName()}} ',
scope : {
config : "=myConfig",
},
link : function(scope, element, attrs, Ctrls){
Ctrls[0].init(Ctrls[1]);
}
}
});
person
artur grzesiak
schedule
09.01.2015