1

I'm sure this question has been asked before but I'm having a hard time figuring out the right thing to search for.

I have an app with a lot of boilerplate code, such as paginators, toggles, filters, etc. I don't want to turn these things into directives as that seems like overkill. Currently I'm using ng-include to dry up my HTML, but in my directives I still have a lot of boilerplate scoped functions.

What I want to know is if/how I can load these functions from a module and have them bound to the scope automatically.

Right now I have this:

.directive('somethingAwesome', ['$http', '$timeout', function($http, $timeout) {
    return {
        replace: true,
        templateUrl: '/assets/awesome_sauce.html',
        transclude: false,
        scope: true,
        controller: ['$scope', '$http', '$timeout', function($scope, $http, $timeout) {
            // Public Functions List
            $scope.next = next;
            $scope.prev = prev;
            $scope.filter = filter;
            // ...

            // Public Functions Definitions
            function next() {
                // Do something
            }
            function prev() {
                // Do something
            }
            function filter() {
                // Do something
            }
            // ...
        }]
    }
}])

I want to do something more like this:

.directive('somethingAwesome', ['$http', '$timeout', function($http, $timeout) {
    return {
        replace: true,
        templateUrl: '/assets/awesome_sauce.html',
        transclude: false,
        scope: true,
        controller: ['$scope', '$http', '$timeout', function($scope, $http, $timeout) {
            include boilerplate;
        }]
    }
}])

(function boilerplate() {
    // Public Functions List
    $scope.next = next;
    $scope.prev = prev;
    $scope.filter = filter;
    // ...

    // Public Functions Definitions
    function next() {
        // Do something
    }
    function prev() {
        // Do something
    }
    function filter() {
        // Do something
    }
    // ...

    return something;
})()

The key here is that simply including boilerplate binds all the functions to the scope of whatever directive or controller included it. Even if I still have to manually bind each function from boilerplate to the scope, something like this is still useful as it drys up a lot of code.

Is this possible, and if so, how?

Daniel Bonnell
  • 4,817
  • 9
  • 48
  • 88

2 Answers2

1

Can you pass in the $scope to the boilerplate function.

.directive('somethingAwesome', ['$http', '$timeout', function($http, $timeout) {
    return {
        replace: true,
        templateUrl: '/assets/awesome_sauce.html',
        transclude: false,
        scope: true,
        controller: ['$scope', '$http', '$timeout', function($scope, $http, $timeout) {
            boilerplate($scope);
        }]
    }
}])

function boilerplate($scope) {
    // Public Functions List
    $scope.next = next;
    $scope.prev = prev;
    $scope.filter = filter;
    // ...

    // Public Functions Definitions
    function next() {
        // Do something
    }
    function prev() {
        // Do something
    }
    function filter() {
        // Do something
    }
    // ...

    return something;
}

or a more angular way is to create a service/factory:

.directive('somethingAwesome', ['$http', '$timeout', 'boilerPlate', function($http, $timeout, boilerPlate) {
    return {
        replace: true,
        templateUrl: '/assets/awesome_sauce.html',
        transclude: false,
        scope: true,
        controller: ['$scope', '$http', '$timeout', function($scope, $http, $timeout) {
            boilerplate.setPagination($scope);
        }]
    }
}])

.factory('boilerPlate', [function(){
  
  return {
      setPagination: function($scope){
        // Public Functions List
        $scope.next = next;
        $scope.prev = prev;
        $scope.filter = filter;
        // ...

        // Public Functions Definitions
        function next() {
            // Do something
        }
        function prev() {
          // Do something
        }
        function filter() {
          // Do something
        }
        // ...
      }
    }
}]);
Hoyen
  • 2,511
  • 1
  • 12
  • 13
1

Another, possibly more angular/OOP way of doing this, is to inherit from the base/"boilerplate" controller using $controller. This has the advantage of letting you use different dependencies for each controller (i.e. if your boilerplate needs $http, but your actual controller doesn't, you do not need to inject $http into your controller, then pass it on to the boilerplate).

This is an example of how to extend controllers (note that the boilerplate controller is initialized, then the inheriting controller. finally, the inheriting controller is able to call $scope.next()):

angular.module('app', [])
.controller('main', function() {})
.controller('baseController', ['$scope', '$http', '$timeout', function($scope, $http, $timeout) {
  console.log('boilerplate controller init');
  $scope.next = function() {
    console.log('next called');
  };
}])
.controller('awesomeController', ['$scope', '$controller', function($scope, $controller) {
  $controller('baseController', {$scope: $scope});
  console.log('awesome controller init');
  $scope.next();
}])
.directive('somethingAwesome', ['$http', '$timeout', function($http, $timeout) {
    return {
        replace: true,
        template: '<p>/assets/awesome_sauce.html</p>',
        transclude: false,
        scope: true,
        controller: 'awesomeController'
    }
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="main">
  <div something-awesome></div>
</div>
tcooc
  • 20,629
  • 3
  • 39
  • 57