请原谅我,如果这听起来很愚蠢,但我现在已经使用AngularJS了一段时间我见过人们告诉我将我的逻辑包装在一个指令(或服务?)而不是我的控制器并只保留我的控制器中的绑定。除了指令的可重用性方面还有其他原因吗?
到目前为止,我还没有真正理解为什么会这样。不写指令会带来很多开销吗?我没有遇到任何在我的控制器中编写逻辑的问题,而且很容易。 这种方法的缺点是什么?
答案 0 :(得分:13)
控制器是完成所有操作以及与范围相关的所有操作的正确位置。这是你写所有
的地方$scope.$watch(...)
并定义您需要从视图中访问的所有$scope
函数(如事件处理程序)。通常,事件处理程序是计划函数,它将依次将函数称为服务。
$scope.onLoginButtonClick = function(){
AuthenticationService.login($scope.username,
$scope.password);
};
在极少数情况下,你可以在那里添加一个promise成功处理程序。
DONT:在控制器中编写业务逻辑
有一个非常具体的原因,为什么早期的例子是这样的。它向您显示了$scope
函数,该函数又调用服务中的函数。控制器不对登录机制或登录方式负责。如果你在服务中编写这个代码,你就会将服务与控制器分离,这意味着你想要使用相同服务的任何其他地方,你需要做的就是注入并解雇该函数。
未来控制器的规则:
答案 1 :(得分:3)
有两个很好的理由让我将逻辑从控制器中移除:
如果你的应用程序有多个控制器,并且每个控制器都有一些差异,那么在控制器中保留逻辑意味着你将重复你编写的代码。如果你Don't Repeat Yourself,那就更好了。通过将该逻辑放入服务中,您可以将相同的代码注入多个控制器。每次将每个服务(实际上为Factory)创建为自身的新实例,每次将其注入控制器。通过将逻辑推送到服务中,您可以modularise代码,这样可以更容易维护和测试(见下文)
测试好的代码。不只是由人,而是由你写的单元测试。单元测试可以让您作为开发人员确保您的代码也能达到您的预期。它们还可以帮助您很好地设计代码。
如果您的控制器有20种不同的方法,每种方法都有自己的逻辑,那么测试(和您的代码)就会变成意大利面。
编写狭窄的单元测试更容易,即他们一次测试一件事。幸运的是,将上面的代码分解为封装的部分也很好(因为上面提到的原因),即他们做了一件事并且可以单独完成。所以单元测试(特别是如果你先编写测试)会迫使你思考如何将你的代码分解为可维护的部分,如果你想在未来进行更改,你的应用程序就会处于良好状态(你运行单元测试)并且可以看到事情在哪里破裂)。
表格申请:
您有一个表单应用程序服务多个表单。每个表单都有一个控制器。当用户提交表单时,数据通过代理发送到CRM,该CRM将信息存储在数据库中。
如果CRM中已存在客户,则您不希望创建重复项(是的,CRM应该处理数据清理,但您希望尽可能避免这种情况)。因此,一旦用户提交表单数据,就需要实现类似以下内容的逻辑:
注意:可以说上述所有内容都应该通过后端服务来完成,但为了举个例子,让我们继续使用它。
您的申请表有多种形式。如果你为每个表单的每个控制器硬编码相同的逻辑(是的,你应该每个表单有一个控制器,即每个视图一个),那么你会多次重复自己。编写需要检查控制器的测试可以完成基础工作(发布数据,管理对视图的更改),还可以测试每个控制器的所有逻辑。
或者将该逻辑写入一次,将其放入服务中,为其编写一个测试并将其注入任何您喜欢的位置。
查看Angular documentation,看看Angular实现的模式以及为什么这些模式可以遵循(Design Patterns - 大模块,依赖注入,工厂和单例)。
答案 2 :(得分:2)
控制器的最大问题是你没有定义它所使用的html。
使用时......
<div ng-controller="myController"></div>
...然后你必须在你的控制器中注入你的html,这是基本的老式jQuery思维。
使用时......
<div ng-controller="myController">... some html ...</div>
...你的指令及其工作的html在不同的地方定义。也不是你想要的。
使用指令强制您将您的html和它所需的代码放在同一个地方。由于该指令也有自己的范围,因此不会对代码中其他地方的其他变量产生任何干扰。如果您需要来自其他地方的变量,您还必须明确地注入它们,这也是一件好事。
我使用的这个词为什么这是一个好东西是'原子'但我不确定这是否是正确的词。含义:所有应该一起工作的东西都在一个文件中。使用templateUrl,这不再完全正确,模板仍然在指令中定义。
所以在我的控制器中没有代码可以对dom做任何事情。只是最简单的一些页面/视图计数代码,或将API数据连接到作用域,或使用$ routeParam数据执行某些操作。所有其他代码都放在服务/工厂(业务逻辑)或指令(dom逻辑)中。
顺便说一句:可以为你的指令定义一个控制器,但通常只用于'指令间通信'(所以它们可以共享状态),但你只能将它与指令一起使用(如tab选项中重复的tab指令。)
答案 3 :(得分:-1)
您不在控制器中编写逻辑的主要原因是$scopes
在路由更改时使用controller
收集垃圾中的所有$destroy()
。在收到ngView
广播的$routeChangeSuccess
指令中,有一个函数只保留当前活动视图的$ scope,所有其他$ scope都被销毁。
因此,例如,如果您有购物车应用程序且您的业务逻辑是使用$ scopes的控制器,则用户将丢失产品和已在订单页面上输入的所有表单数据(如果他们使用后退按钮等)