我听说在使用隔离范围的指令中使用controllerAs
语法和bindToController: true
是一个好习惯。参考文献:one,two
假设我有这样的指示:
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,有没有人能够了解最近的最佳做法?
答案 0 :(得分:14)
我认为最好的做法是在指令的控制器中移动初始化代码和/或公开API函数,因为它有两个目的:
1. Intialization of $scope
2. Exposing an API for communication between directives
假设您的指令定义了子范围(或继承范围)。如果初始化链接函数内部的作用域,则子作用域将无法通过作用域继承访问此处定义的任何作用域变量。这是因为父链接功能总是在子链接功能之后执行。因此,范围初始化的适当位置在控制器功能内部。
子指令可以通过指令定义对象上的'require'属性访问父指令的控制器。这允许指令进行通信。为了使其工作,必须完全定义父控制器,以便可以从子指令的链接函数访问它。实现这一点的最佳位置是控制器功能本身的定义。父控制器函数总是在子控制器函数之前调用。
重要的是要理解链接功能和控制器功能有两个非常不同的用途。控制器功能是为初始化和指令通信而设计的,链接器功能是为运行时行为而设计的。根据代码的意图,您应该能够确定它是属于控制器,还是属于链接器。
您是否应该将任何初始化范围的代码从链接功能移动到控制器功能?
是的,这是控制器功能存在的主要原因之一:初始化范围,并允许其范围参与原型范围继承。
您应该将$ watch处理程序从链接功能移动到控制器功能吗?
没有。链接函数的目的是连接行为并可能操纵DOM。在link函数中,所有指令都已编译,并且所有子链接函数都已执行。这使得它成为一个理想的连接行为的地方,因为它尽可能接近DOM准备就绪(直到渲染阶段之后才真正准备好DOM)。
答案 1 :(得分:3)
我将从你的最后一句话开始。这就是你想要如何编写角度代码的全部内容。如果你想坚持为角度1.x编写好的代码的指南,那么甚至不用太在意什么是理想的。但是,如果您想为下一版本的Angular以及即将推出的Web技术做准备,我建议您开始采用新概念并根据您今天编写代码的方式进行调整。在这种情况下,请记住没有对错。
谈到angular 2.0和ES6,我想强调指令的概念将更符合Web Components技术。
在Angular 2.0中(根据当前的设计)将摆脱定义指令的复杂方式;那不再是DDO。因此,如果你开始以这种方式思考,我认为会更好。组件只有一个View和一个控制器。
例如,
@ComponentDirective({
selector:'carousel',
directives:[NgRepeat]
})
export class Carousel{
constructor(panes:Query<CarouselItem>) {
this.items= panes;
}
select(selectedCarouselItem:CarouselItem) { ... }
}
上面的代码是用AtScript(打字稿和ES6的超集)编写的,但你也可以在ES5中表达同样的东西。你可以看到事情会变得多么简单。在np这样的概念,如链接功能或编译等。
另外,上述组件的视图将直接绑定到上述类;因此,您已经可以找到与controllerAs语法的相似性。
所以从本质上讲,我建议你先看看Web Components背后的一般思路,以及Web Developments的未来如何,然后我想你会开始编写Angular 1.x代码。 。
总之,尝试以有利于Angular当前版本的方式编写代码,但是如果您认为代码的某些部分可以包含下一版本的某些概念,那么请执行此操作。我不相信它会伤害你。尽量保持简单,因为新版本的Angular会更简单。
我建议您阅读以下帖子:
答案 2 :(得分:2)
(在底部我添加了一个显示方法的代码/ 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/(它提倡< em>最少使用链接函数并使用ui-bootstrap作为使用此类模式的示例)。我不能同意这两篇文章。
关于Angular2.0的另一件事:在angular2.0中不再有$scope
- 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
方法的情况下,没有这样的问题。为什么重要:
那么必须在链接功能中添加什么:
$element
暴露$on('linked')
事件,则基本上此点无效。require:
的引用。再次,如果可以将它们直接注入controller
...... 不过,我更喜欢在controller
中定义所有函数,只需通过link
的范围调用它们。理想情况下,这只是一个电话:scope.init
。
现在回答您的问题是否应将link
功能转换为controller
。它确实取决于许多标准,但如果代码是积极开发的,那么可能值得考虑。我的经历(以及我谈过的几个人)可以用这张图片来说明:
关于angular2.0 - 它将是一个构造转变,所以从这个角度看它应该没什么关系,但controller
的方法似乎更接近于指令/组件的方式将通过ES6课程在v2.0中声明。
最后一件事:在某种程度上,这是一个品味问题,但有一些正当理由让 CONTROLLER
功能变薄(通过将逻辑委托给服务) )。
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]);
}
}
});
答案 3 :(得分:-1)
根据最新的documentation,这仍然是推荐的做法&#34;当您想要将API暴露给其他指令时使用控制器。否则使用链接。&#34;我也想听听别人和他们使用的方法。
答案 4 :(得分:-1)
分享here的内容,(我没有足够的声誉将其作为评论)
“where do I put code, in ‘controller’ or ‘link’?”
Couple of things to note:
控制器'$ scope'和链接'scope'是一回事。不同之处在于发送给控制器的参数通过依赖注入来实现(因此需要调用'$ scope'),其中发送到链接的参数是基于标准顺序的函数。在上下文中,所有角度示例都将使用“范围”,但出于理智原因,我通常将其称为范围:http://plnkr.co/edit/lqcoJj?p=preview
此示例中的$ scope / scope只是从父控制器传入的范围。
'link'实际上是'post-link'函数(参见下面的渲染管道)。由于很少使用预链接,因此“链接”选项只是设置“后链接”功能的快捷方式。
那么,最新的例子是什么?好吧,当我决定的时候,我会这样做:
答案归功于 jasonmore