AngularJS DRY控制器结构

时间:2014-07-10 12:41:52

标签: angularjs controller dry restangular

下面的代码表示在处理来自服务器的数据的每个控制器中重复相同代码模式的情况。经过长时间的研究和#angularjs的irc谈话,我仍然无法想象如何抽象代码,内联评论解释了这些情况:

myApp.controller("TodoCtrl", function($scope, Restangular,
                                      CalendarService, $filter){
    var all_todos = [];
    $scope.todos = [];
    Restangular.all("vtodo/").getList().then(function(data){
        all_todos = data;
        $scope.todos = $filter("calendaractive")(all_todos);
    });
    //I can see myself repeating this line in every 
    //controller dealing with data which somehow relates
    //and is possibly filtered by CalendarService:
    $scope.activeData = CalendarService.activeData;
    //also this line, which triggers refiltering when
    //CalendarService is repeating within other controllers
    $scope.$watch("activeData", function(){
        $scope.todos = $filter("calendaractive")(all_todos);
    }, true);

});

//example. another controller, different data, same relation with calendar?
myApp.controller("DiaryCtrl", function($scope, Restangular,
                                       CalendarService, $filter){
    //this all_object and object seems repetitive,
    //isn't there another way to do it? so I can keep it DRY?
    var all_todos = [];
    $scope.todos = [];
    Restangular.all("diary/").getList().then(function(data){
        all_diaries = data;
        $scope.diaries = $filter("calendaractive")(all_diaries);
    });
    $scope.activeData = CalendarService.activeData;
    $scope.$watch("activeData", function(){
        $scope.todos = $filter("calendaractive")(all_diaries);
    }, true);
});

4 个答案:

答案 0 :(得分:12)

应该有目的地遵循DRY,而不是热心。你的代码很好,控制器正在做他们应该做的事情:连接应用程序的不同部分。也就是说,您可以轻松在返回函数引用的工厂方法中组合重复的代码。

例如,

myApp.factory('calendarScopeDecorator', function(CalendarService, Restangular, $filter) {
  return function($scope, section) {
    $scope.todos = [];
    $scope.activeData = CalendarService.activeData;
    Restangular.all(section+"/").getList().then(function(data){
      $scope.all_data = data;
      $scope.filtered_data = $filter("calendaractive")(data);
    });
    $scope.$watch("activeData", function(){
      $scope.todos = $filter("calendaractive")($scope.all_data);
    }, true);
  }
});

然后将其合并到您的控制器中:

myApp.controller("DiaryCtrl", function($scope, calendarScopeDecorator){
  calendarScopeDecorator($scope, 'diary');
});

答案 1 :(得分:3)

我不会像观察者那样使用观察者和本地参考来做这种事情。相反,我会使用$ on()/ $ emit()来建立从服务到关心其更新的控制器的pub-sub模式。这是一种未充分利用的模式IMO,它提供了更“干”的机制。它也非常高效 - 通常比观察者更有效率,因为它不需要运行摘要来知道某些事情已发生变化。在基于网络的服务中,您几乎总是可以确定地知道这一点,并且您不需要知道它肯定会将其隐含在其他位置。这样可以避免Angular深度检查对象的成本:

$rootScope.$on('calendarDiariesUpdated', function() {
    // Update your $scope.todos here.
}, true);

在您的服务中:

// When you have a situation where you know the data has been updated:
$rootScope.$emit('calendarDiariesUpdated');

请注意,emit / on比使用broadcast更有效,后者将遍历所有嵌套范围。您也可以通过这种方式将数据从服务传递到侦听控制器。

这是一项非常重要的技术,可以做一些事情:

  1. 您不再需要对activeData进行本地引用,因为您实际上并未使用它(它是干的)。

  2. 这在大多数情况下比观察者更有效。 Angular不需要知道你需要被告知更新 - 你知道你做了。这也是一种干燥的原则 - 为什么要使用框架工具来做你实际上不需要的东西?将数据放到某处然后等待Angular消化它并说“哇,你需要知道这个。”

  3. 这是一个额外的步骤。
  4. 您甚至可以减少注射量。没有必要使用CalendarService,因为该服务可以在其通知中直接传递对数组的引用。这很好,因为如果您更改服务中的存储模型,您不需要稍后重构(DRY倡导者也提倡的事情之一是抽象这些东西)。

  5. 你需要使用$ rootScope以便注册观察者,但pub-sub概念中没有任何内容违反DRY。这是非常普遍和被接受的(最重要的是:它表现得非常好)。这与原始全局变量不同,后者首先使用$ rootScope吓坏了人们(通常是正确的)。

    如果您想成为“Super DRY”,您可以将对$ filter的调用重新分配到执行过滤的单个方法中,并从REST承诺解析和日历更新通知中调用它。这实际上增加了几行代码......但不重复任何代码。 :)原则上这可能是一个好主意,因为那个特定的行是你可能会维护的东西(它需要静态的“calendaractive”参数...)

答案 2 :(得分:1)

看起来2控制器中的代码是相同的,除了API调用的路径:“vtodo /”或“diary /".

实现更接近DRY-ness的一种方法是将API路径作为选项传递给控制器​​作为属性。因此,假设我们调用控制器ApiController,这可以用作

<div ng-controller="ApiController" api-controller-path="vtodo/">
  <!-- Todo template -->
</div> 

<div ng-controller="ApiController" api-controller-path="diary/">
  <!-- Diary template -->
</div> 

然后可以通过注入的$attrs参数在控​​制器中访问:

myApp.controller("ApiController", function($scope, $attrs, Restangular, CalendarService, $filter) {
  // "vtodo/" or "diary/"
  var apiPath = $attrs.apiControllerPath;

作为一个警告,我要注意过度架构,不是所有事情都需要考虑因素,并且有一种观点认为你只是遵循设计模式而不是复制+粘贴代码。但是,我已经使用上面的方法将选项传递给控制器​​以获得类似的情况。

答案 3 :(得分:0)

如果您担心多次调用同一资源CalendarService,我建议您找一种缓存结果的方法,例如在该服务中定义变量或使用Angular&#39; s $cacheFactory

否则,我的模式中没有任何错误