AngularJS干式控制器结构

AngularJS干式控制器结构,angularjs,controller,dry,restangular,Angularjs,Controller,Dry,Restangular,下面的代码表示在处理服务器数据的每个控制器中重复相同代码模式的情况。在#angularjs进行了长时间的研究和irc演讲后,我仍然无法理解如何抽象代码,内联注释解释了以下情况: myApp.controller("TodoCtrl", function($scope, Restangular, CalendarService, $filter){ var all_todos = []; $scope.to

下面的代码表示在处理服务器数据的每个控制器中重复相同代码模式的情况。在#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);
});

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


否则,我看不出您的模式有任何问题。

我不会像在这个控制器中那样使用监视程序和本地引用来做这种事情。相反,我将使用$on()/$emit()来建立一个从服务输出到关心其更新的控制器的发布子模式。这是一种使用不足的模式,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比使用广播更有效,广播将遍历所有嵌套作用域。您还可以通过这种方式将数据从服务传递到侦听控制器

这是一项非常重要的技术,可以做以下几件事:

  • 您不再需要对activeData进行本地引用,因为您实际上没有使用它(它是干的)

  • 在大多数/许多情况下,这比观察者更有效。Angular不需要计算出你需要被告知更新-你知道你需要。这也是一种枯燥的原则——为什么要使用框架工具来做一些实际上不需要的事情?这是一个额外的步骤,将数据放在某个地方,然后等待Angular消化并说“哇,你需要知道这一点。”

  • 你甚至可以减少注射。不需要使用CalendarService,因为该服务可以在其通知中传递对数组的引用。这很好,因为如果以后更改服务中的存储模型,就不需要重构它(DRY倡导者也提倡将这些东西抽象出来)

  • 您确实需要使用$rootScope才能注册观察者,但pub-sub概念中没有违反DRY的内容。这是非常普遍和被接受的(最重要的是:它表现得非常好)。这与原始全局变量不同,它首先会让人们害怕使用$rootScope(通常是正确的)


    如果您想成为“超级干燥”,您可以将对$filter的调用重新分解为一个进行过滤的方法,并从REST promise解析和日历更新通知中调用它。这实际上增加了几行代码。。。但不重复任何内容。:)原则上,这可能是一个好主意,因为您可能要维护特定的行(它需要静态的“calendaractive”参数…)

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

    例如 然后将其合并到控制器中:
    看起来这两个控制器中的代码是相同的,只是API调用的路径不同:“vtodo/”或“diary/”

    实现更接近干性的方法之一是将API路径作为选项作为属性传递给控制器。因此,假设我们调用controller
    ApiController
    ,它可以用作

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

    作为一个警告,我要小心过度的架构设计,并不是所有的东西都需要考虑,而且有一个论点认为您只是遵循一个设计模式,而不是复制+粘贴代码。但是,对于类似的情况,我自己也使用了上述方法将选项传递给控制器。

    问题不在于CalendarService。CalendarService是一个将返回活动日历的单例服务,我的问题不是多次调用此服务。。我所关心的是控制器的非干涸方法,在这里我必须重新观看calendarservice.activeData,我想这可能是抽象的……我明白你的意思。我确实误解了。我的猜测是,由于该服务正在跟踪更新,而
    $watch
    是让控制器知道发生了某些更改的唯一方法,因此这可能是最好的方法。当我的应用程序中的某些上下文通过使用服务发生更改时,我实际上使用了类似的设置。最后,我让每个控制器都监视这个变量,以更新范围的数据模型。我找不到另一个方向。。他们有很多相似的部分,但没有那么多。。它们将运行不同的过滤器,具有不同的函数调用,等等。非常好,我将立即实现这个工厂,因为它的功能将在许多控制器中重用。我将使用@Chad对$emit/$on的解释来实现您建议的工厂。t请注意,使用
    $emit
    不再比
    $broadcast
    更有效。这是angular早期版本中的一个问题,但如果您使用的是angular 1.2或1.3的最新版本,则问题已得到解决。在我的应用程序上下文中,我不得不使用$broadcast,因为$emit没有达到我需要的目标!我真的认为你的话会帮助很多其他angularjs开发者。我正在重新分解代码,以便在/emmit上使用,而不是查看对象。我真的不喜欢我看电视的方式
    myApp.controller("DiaryCtrl", function($scope, calendarScopeDecorator){
      calendarScopeDecorator($scope, 'diary');
    });
    
    <div ng-controller="ApiController" api-controller-path="vtodo/">
      <!-- Todo template -->
    </div> 
    
    <div ng-controller="ApiController" api-controller-path="diary/">
      <!-- Diary template -->
    </div> 
    
    myApp.controller("ApiController", function($scope, $attrs, Restangular, CalendarService, $filter) {
      // "vtodo/" or "diary/"
      var apiPath = $attrs.apiControllerPath;