4

Im using angular to build a big app and i have some common methods for controllers actualy im doing this, but exist a best way to do it?

app.controller('baseController', function($scope, $controller, appFactory) {
    var $scope.foo = function() {
        // Do something
    }
});


app.controller('childController', function($scope, $controller, appFactory) {

    // Here i extend or something like, the base controller

    $controller('baseController', {$scope: $scope});

    var $scope.bar = function() {
        // Do a lot of things an then call foo
        $scope.foo();
    }
}):

i do it because this methods need to have de $scope of my controller.

Victor Huerta
  • 194
  • 1
  • 6
  • 1
    Generally common methods should go into a provider (service,factory,...). Keep in mind Angular is designed around thin controllers that just "mediate between view and model". If you can give some more specifics I might be able to give you more concrete advice – KayakDave Dec 23 '13 at 20:41
  • Please see this thread http://stackoverflow.com/questions/16539999/angular-extending-controller – user2923779 Dec 23 '13 at 20:54

2 Answers2

3

I dont agree with the above comments that inheritance should not be implemented for controllers. There are many cases where controller inheritance will keep your code DRY even if you use shared Service/Factory/Providers. Using @Clever's answer's example, why would you want to repeat $scope.foo = function() { MyFactory.calculateFoo(); } in X number of controllers, if you could put it into a single base controller. That does not make the controller fat, instead it cleans it up and keeps it DRY.

Misko himself gave this example in the AngularJS google group on one possible implementation of controller inheritance. Personally I use this method myself, simple example, called from within my child controller:

$injector.invoke(MyBaseController, this, { $scope: $scope, alertService: alertService });

An example of where I use controller inheritance, for most of my CRUD pages I have a single parent controller that implements the generic create/update $scope functions. This base controller gets a repository service injected that it uses to do the actual server calls etc. Why repeat this over and over for all your CRUD pages?

Beyers
  • 8,968
  • 43
  • 56
  • I agree there are cases where something like this really interesting example you showed is good from a dry standpoint. But I worry that it makes it easy to move away from the thin controller ideal. In your specific example the question I'd have is- do server calls count as "business logic"? If so, the advice I've seen given by the Angular team is business logic should go into providers and controller's only job should be to map those results on to the view. Great architecture discussion! – KayakDave Dec 23 '13 at 21:34
  • @KayakDave I also find this topic interesting. In my example the server calls and any business logic is actually still implemented inside providers, the base controller just implements the generic/shared mapping of the provider functionality and results on to the view. So I believe it conforms to the general advice given. – Beyers Dec 23 '13 at 22:07
  • I don't see a use case for a base controller. Models and business logic are defined with their corresponding repositories. Why would the controller even have create/update $scope? If you have a repeated component that expects something on the controller, just include a controller or a directive with that part of the template. – FlavorScape Jun 24 '14 at 23:29
  • @FlavorScape Simple example, you want to display a notification message after you have updated your model. Surely you dont want to pollute your model/repository with displaying UI messages. So in this case your controller can have an update scope function, which will handle the actual update via your repository, and display the notification via your notification service upon repository update success/failure. There are many similar examples that are all valid reasons for this. – Beyers Jun 25 '14 at 01:10
  • @Beyers All you'd need in this case is a $watch on your model to dispatch an event to your global notification directive. There's no need to subclass here. Or you could make a notification service and re-use it in all your controllers (explicit injection vs implicit inheritance). I see no case for inclusion polymorphism for controllers. You could even include a generic directive in your forms that watch for scope updates and manage the notification view. – FlavorScape Jun 25 '14 at 17:17
  • The goal is to just make controllers a thin layer of glue. Modularize your functionality with directives. Here's an example: https://gist.github.com/vance/db40c407fdb81ba762ca – FlavorScape Jun 25 '14 at 17:40
  • I've recently made a NotificaitonService and have a NotificationController watching this. Then when the promise comes back from model update, we call a function in NotificationService and the presentation layer figures out what to show. – FlavorScape Jun 25 '14 at 20:04
  • @FlavorScape your last example is a good use case for what I'm saying. _Then when the promise comes back from model update, we call a function in NotificationService and the presentation layer figures out what to show._ <- Controller waits for promise and then calls notifications service. But to be honest it's getting a bit pedantic, whether you glue this together in a base controller or via directive or wherever, it just glue. I prefer that glue in a base controller. There is no right or wrong here IMHO. – Beyers Jun 25 '14 at 20:12
1

To elaborate on what KayakDave said, Angular controllers are generally lightweight. If you find yourself thinking "Inheritance" within the controller, you are probably doing it wrong. It is better to extract common logic shared between controllers into a Service/Factory/Provider. For example:

app.Factory('MyFactory', function() {
    return {
        calculateFoo: function() { 
          // stuff 
        }
    };

});

app.controller('FirstCtrl', function($scope, $controller, MyFactory) {
    var $scope.foo = function() {
        MyFactory.calculateFoo();
    }
});


app.controller('SecondCtrl', function($scope, $controller, MyFactory) {

    var $scope.bar = function() {
        MyFactory.calculateFoo();
    }
}):
Community
  • 1
  • 1
Clev3r
  • 1,568
  • 1
  • 15
  • 28