AngularJS: Должен ли я преобразовать функцию привязки директивы к контроллеру?
Я слышал, что рекомендуется использовать синтаксис controllerAs
вместе с bindToController: true
в директивах, использующих область изоляции. Ссылки: один, два
Предположим, у меня есть такая директива:
angular.module('MyModule').directive('MyDirective', function(User) {
return {
scope: {
name: '='
},
templateUrl: 'my-template.html',
link: function(scope) {
scope.User = User;
scope.doSomething = function() {
// Do something cool
};
}
};
});
<!-- my-template.html -->
<div>
User Id: {{ User.id }}
Name: {{ name }}
<button ng-click="doSomething()">Do it</button>
</div>
Как вы можете видеть, в этой директиве нет контроллера. Но, чтобы иметь возможность использовать controllerAs
и bindToController: true
, у меня должен быть контроллер.
Лучше всего преобразовать функцию связывания в контроллер?
angular.module('MyModule').directive('MyDirective', function(User) {
return {
scope: {
name: '='
},
templateUrl: 'my-template.html',
bindToController: true,
controllerAs: 'myCtrl',
controller: function() {
this.User = User;
this.doSomething = function() {
// Do something cool
};
}
};
});
<!-- my-template.html -->
<div>
User Id: {{ myCtrl.User.id }}
Name: {{ myCtrl.name }}
<button ng-click="myCtrl.doSomething()">Do it</button>
</div>
Мое понимание заключается в том, что контроллер директивы должен использоваться как механизм для раскрытия директивного API для директив-директивной связи.
Может ли кто-нибудь пролить свет на то, что лучше всего в наши дни, имея в виду Angular 2.0?
Ответы
Ответ 1
Я считаю целесообразным переместить код инициализации и/или выставить функции API внутри директивного контроллера, потому что он выполняет две цели:
1. Intialization of $scope
2. Exposing an API for communication between directives
Инициализация области
Предположим, что ваша директива определяет дочернюю область (или наследует область). Если вы инициализируете область видимости внутри своей функции ссылок, то дочерние области не смогут получить доступ к переменным области видимости, определенным здесь, через наследование области. Это связано с тем, что функция родительской ссылки всегда выполняется после функции дочерней ссылки. По этой причине правильное место для инициализации области находится внутри функции контроллера.
Предоставление API-интерфейса контроллера
Директивы Child могут обращаться к родительскому директивному контроллеру через свойство require для объекта определения директивы. Это позволяет директивам общаться. Чтобы это работало, родительский контроллер должен быть полностью определен, так что к нему можно получить доступ из дочерней директивной ссылки. Лучшее место для реализации этого - это определение самой функции контроллера. Функции родительского контроллера всегда вызываются перед функциями дочернего контроллера.
Заключительные мысли
Важно понимать, что функция связи и функция контроллера служат для двух разных целей. Функция контроллера была разработана для инициализации и директивной связи, а функция компоновщика была разработана для поведения во время выполнения. Основываясь на намерении вашего кода, вы должны решить, принадлежит ли он в контроллере или принадлежит линкеру.
Следует ли переместить какой-либо код, который инициализирует область действия функции ссылки на функцию контроллера?
Да, это одна из основных причин, по которым существует функция контроллера: инициализировать область действия и разрешить ее область действия участвовать в наследовании прототипа.
Должны ли вы переместить $watch-обработчики из функции ссылок на функцию контроллера?
Нет. Назначение функции ссылки - это поведение при подключении и потенциальная манипуляция с DOM. В функции ссылки все директивы были скомпилированы, и все дочерние функции ссылок уже выполнены. Это делает его идеальным местом для взаимодействия, потому что он как можно ближе к DOM (он не является DOM полностью до фазы Render).
Ответ 2
Я начну с вашего последнего предложения. Все о том, как вы хотите написать свой код angular. Если вы хотите придерживаться рекомендации по написанию хорошего кода для angular 1.x, то даже не думайте слишком много о том, что идеально. Однако, если вы хотите подготовиться к следующей версии Angular, а также к предстоящим веб-технологиям, я бы предложил начать использовать новые концепции и настроить их так, как вы пишете свой код сегодня. В этом случае нет ничего правильного или неправильного.
Говоря о angular 2.0 и ES6, я хотел бы подчеркнуть, что понятие директив будет в большей степени соответствовать технологии веб-компонентов.
В angular 2.0 (в соответствии с текущим проектом) избавится от сложного способа определения директив; Это больше не DDO. Таким образом, я думаю, было бы лучше, если вы начнете думать таким образом. Компонент будет иметь только View и контроллер.
Например,
@ComponentDirective({
selector:'carousel',
directives:[NgRepeat]
})
export class Carousel{
constructor(panes:Query<CarouselItem>) {
this.items= panes;
}
select(selectedCarouselItem:CarouselItem) { ... }
}
Вышеприведенный код написан на языке AtScript (надмножество typescript и ES6), но вы также сможете выразить то же самое в ES5. Вы можете видеть, насколько просты будут вещи. Там в np такое понятие, как функция ссылки или компиляция и т.д.
Кроме того, представление вышеуказанного компонента будет непосредственно связано с указанным выше классом; Таким образом, вы уже можете найти сходство с синтаксисом controllerAs.
Итак, по сути, я бы предложил сначала взглянуть на общую идею веб-компонентов и как будущее веб-разработок может быть, и тогда я думаю, что вы начнете писать код angular 1.x с тем в уме.
В общем, попробуйте сделать код таким образом, чтобы он поддерживал текущую версию Angular, но если вы считаете, что есть некоторые части вашего кода, которые могут охватывать некоторые концепции следующей версии, тогда сделайте это. Я не верю, что это навредит тебе. Постарайтесь сохранить это просто, поскольку новая версия angular будет проще.
Я бы предположил, что вы читаете следующие сообщения:
Ответ 3
UPDATE
(внизу я добавил код /plnkr, который показывает подход)
Помимо упомянутой статьи: https://www.airpair.com/angularjs/posts/preparing-for-the-future-of-angularjs#3-3-match-controllers-with-directives, который в основном не только защищает шаблон, который вы запрашиваете, но и компонентный фронт -end в целом, я нашел: 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
(вы все равно можете использовать те, которые были введены в главную директивную функцию). В случае подхода controller
такой проблемы нет. Почему это важно:
- он сохраняет лучшую структуру кода, имея зависимости ближе к контексту, в котором они нужны.
- люди, приходящие на angular с фонами, отличными от JS, все еще испытывают проблемы с функционированием функционального закрытия в JS
Итак, что нужно добавить в функцию ссылки:
- Все, что нужно запустить после того, как элемент был вставлен в DOM. Если
$element
выставлено событие $on('linked')
, чем в основном, эта точка недействительна.
- Захват ссылок на контроллеры
require:
ed. Опять же, если бы можно было сразу вставить их в controller
...
Тем не менее, я предпочитаю определять все функции в controller
и просто вызывать их через область link
. В идеале это всего лишь один вызов: scope.init
.
Misko Hevery сказал пару раз, что DDO далек от того, чтобы быть совершенным и понятным, и он развился до того, что сейчас. Я уверен, что если бы проектные решения были сделаны заранее, тогда было бы единственное место, чтобы поставить логику директивы - как это будет в Angular2.0.
Теперь отвечая на ваш вопрос, если вы должны преобразовать функцию link
в controller
. Это действительно зависит от ряда критериев, но если код активно развивается, то, вероятно, стоит рассмотреть. Мой опыт (и пару людей, о которых я говорил об этом) можно проиллюстрировать следующим образом: ![The Link Mess Zenith]()
О Angular2.0 - это будет тектонический сдвиг, поэтому с этой точки зрения это не имеет большого значения, но все же подход controller
кажется ближе к тому, как директивы/компоненты собираются объявляется в версии 2.0 через классы ES6.
И как последнее: в какой-то степени это вопрос вкуса, но есть некоторые веские причины для того, чтобы функция controller
была тонкой (путем делегирования логики сервисам).
UPDATE - 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]);
}
}
});
Ответ 4
В соответствии с последней документацией это по-прежнему рекомендуемая практика "использовать контроллер, если вы хотите открыть API для других директив. В противном случае используйте ссылку". Я хотел бы также услышать от других людей и подход, который они используют.
Ответ 5
поделиться содержимым здесь, (у меня нет достаточной репутации, чтобы поместить его в комментарии)
"where do I put code, in ‘controller’ or ‘link’?"
- Перед компиляцией? - Контроллер
- После компиляции? - Ссылка
Couple of things to note:
-
Контрольная область $scope и link - одна и та же. Разница заключается в том, что параметры, отправленные контроллеру, попадают туда через Injection Dependency (поэтому требуется его "$ scope" ), где параметры, отправленные по ссылке, являются стандартными функциями на основе порядка. Все примеры angular будут использовать "область действия" в контексте, но я обычно называю ее "$ scope" для разумных причин: http://plnkr.co/edit/lqcoJj?p=preview
-
$scope/scope в этом примере - это просто тот, который передается из родительского контроллера.
-
'ссылка в директивах на самом деле является "функцией пост-ссылки" (см. ниже конвейер рендеринга). Поскольку предварительная ссылка редко используется, опция "ссылка" - это просто ярлык для настройки функции пост-ссылки.
Итак, что такое настоящий мир? Ну, когда я решаю, я иду по этому поводу:
- "Я просто делаю шаблоны и рамки?" - переходит в контроллер
- "Я добавляю библиотеку jquery coolbeans?" - идет по ссылке
Кредит для ответа идет на jasonmore