应该将angularjs指令直接与服务交互还是将其视为反模式?


35

哪个更好:

  • 具有直接与服务交互的指令

要么

  • 是否有一条指令公开了某些挂钩,控制器可以将行为绑定到该挂钩(涉及服务)?

我将需要更多有关您要实现的目标,要传达的内容,可以预见的源代码的范围,您的域是什么,它必须如何扩展?
用户

这是一个负责呈现注释小部件的指令-它显示注释字段以及提交/取消按钮。假定仅在一个上下文中使用此指令-注释“文档”。控制器当前的处理方式是公开用于创建实际注释的功能(控制器get的注释服务注入实例)。另一种方法是将整个内容(以及错误/成功处理)封装在指令中(指令将注入注释服务)。
WTK

Answers:


24

当指令简短(按代码),(可能)可重用且在功能方面有一定范围时,它是最佳的(作为经验法则)。制定一个包含UI并依赖于服务的指令(我假设该指令处理与后端的连接),不仅赋予它2个功能角色,即:

  • 控制UI以显示/输入小部件的数据。
  • 提交到后端(通过服务)。

但也会降低它的可重用性,因为这样您就不能再将其与其他服务或不同的UI一起使用(至少不容易)。

当作出这些决定时,我经常比较内置的HTML元素:例如<input><textarea>或者<form>:他们是完全独立于任何特定的后端。HTML5为<input>元素提供了一些额外的类型,例如date,它仍然独立于后端,以及数据的确切位置或使用方式。它们纯粹是界面元素。我认为,使用指令构建的自定义小部件(如果可能)应该遵循相同的模式。

但是,这还不是故事的结局。除了内置HTML元素的类比之外,您还可以创建可重用的指令,它们既调用服务,又使用纯UI指令,就像它可能使用<textarea>。假设您要使用一些HTML,如下所示:

<document document-url="'documents/3345.html'">
 <document-data></document-data>
 <comments></comments>
 <comment-entry></comment-entry>
</document>

要编写commentEntry指令,可以制作一个非常小的指令,仅包含将服务与UI小部件链接起来的控制器。就像是:

app.directive('commentEntry', function (myService) {
  return {
    restrict: 'E',
    template: '<comment-widget on-save="save(data)" on-cancel="cancel()"></comment-widget>',
    require: '^document',
    link: function (scope, iElement, iAttrs, documentController) {
      // Allow the controller here to access the document controller
      scope.documentController = documentController;
    },
    controller: function ($scope) {
      $scope.save = function (data) {
        // Assuming the document controller exposes a function "getUrl"
        var url = $scope.documentController.getUrl(); 

        myService.saveComments(url, data).then(function (result) {
          // Do something
        });
      };
    }
  };
});

极端地讲,您可能永远不需要ng-controller在HTML中具有手动属性:只要每个指令都直接具有明确的“ UI”角色或明确的“数据”角色,就可以使用指令来完成所有操作。

我应该提到一个缺点:它为应用程序提供了更多的“活动部件”,这增加了一些复杂性。但是,如果每个部分都有明确的作用并且很好(单元+端到端测试),那么我认为这是值得的,并且从长远来看会带来整体收益。


59

请允许我不同意Michal Charemza的回答。

尽管他的回答在理论上是正确的,但对于现实世界来说却不是很实用。

我之所以这样说是因为我曾经这样想,并试图在我和我的团队正在构建的大型现实应用程序中执行它,而这太麻烦了。

与HTML语言的类比不好,因为您不应该努力构建通用的,可重用的指令,因为您没有在构建像Web浏览器这样的通用应用程序。

相反,您应该使用指令为您的应用程序构建一个域专用语言(DSL),该语言驻留在其自己的域中。

这并不意味着所有指令都不应该是通用的。如果有其本质,则可能会有一些。如果您要构建自定义日期选择器,则一定要使其通用且可在各个应用程序之间重用。

但是,如果您要构建类似于绑定到您的后端的登录框之类的东西,那就去做吧。

唯一的经验法则是:永远不要重复代码(将小片段抽象到工厂和服务中)并通过依赖注入使其可测试。幸运的是,有了Angular,这些都是小菜一碟。

把事情简单化。:)


5
优点Dema-尽管我接受了Michal的回答,但我同意您的方法,但我们不应该仅仅为了使之重用而使某些东西可重复使用。将服务与指令绑定确实是我最初的本能,因为这很有意义,而不是因为angularjs专家会或不会这样做。最后,我创建了带有直接注入服务的指令,并且作为公共API,我提供了一个挂钩,该挂钩用于在实际创建注释后触发的回调。
WTK 2014年

2

我认为“指令应该与服务交互”问题取决于您的服务在做什么。

我已经将指令与不对HTTP请求执行任何操作的服务进行交互,我认为这是一个很好的模式。服务/工厂对于封装更多面向数据的逻辑非常有用,而指令对于封装面向表示的逻辑非常有用。Angular文档中服务的陈述目的是:“您可以使用服务来组织和共享应用程序中的代码。”。范围很广,但是可以在指令中使用服务来实现该目标。

话虽这么说,但我确实理解在某些情况下希望这样做的要求,以便指令不会直接发出任何HTTP请求。同样,这取决于服务以及您如何组织服务。


1

根据AngularJS框架,我们应该单例工厂/服务以从服务器获取任何数据。这样,这些工厂就可以在整个应用程序中重用而无需重写它们。在内部指令中,我们可以调用这些工厂来获取从Api /服务器获取的数据。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.