Search code examples
angularjscontrollerdryrestangular

AngularJS DRY controller structure


The code below represents a situation where the same code pattern repeats in every controller which handles data from the server. After a long research and irc talk at #angularjs I still cannot figure how to abstract that code, inline comments explain the situations:

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);
});

Solution

  • DRY should be followed purposefully, not zealously. Your code is fine, the controllers are doing what they are supposed to be doing: connecting different pieces of the app. That said, you can easily combine repeated code in a factory method that returns a function reference.

    For example,

    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);
      }
    });
    

    And then incorporate this into your controller:

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