19

I have an Angular application. Its working good, but as my application is getting bigger I'm worried about the large number of dependencies that I have to inject in each controller.

for example

app.controller('viewapps',[
    '$scope','Appfactory','Menu','$timeout','filterFilter','Notice', '$routeParams', 
    function($scope,Appfactory,Menu,$timeout,filterFilter,Notice,$routeParams) {
        //controller code..    
}])

I am sure that the list of dependencies are going to increase in future. Am I doing something wrong here? Is this the right approach? What is the best way to effectively handle this?

Steven
  • 166,672
  • 24
  • 332
  • 435
harikrish
  • 2,009
  • 3
  • 19
  • 37

4 Answers4

12

It's hard to be specific without an exact use case, or seeing the exact code in your controller, but it looks like your controller might be doing too much (or might end up doing too much as you add things later). 3 things you can do:

  • Delegate more of the logic to service(s) that are injected in.

  • Separate out into different controllers, so each only has (just about) 1 responsibility.

  • Separate out into directives, each with their own controllers and templates, and allow options to be passed in, and output given out, via attributes and the scope option of the directive. This is often my preferred option, as you end up building a suite of reusable components, each with a mini-API.

    It is fine for directives to be used like this, at least in my opinion. They aren't just for handling raw Javascript events, or accessing the DOM directly.

Michal Charemza
  • 25,940
  • 14
  • 98
  • 165
  • i had a factory for every DB table.In one controller i needed to query out 6 table and i had to inject 6 factories+$location+$scope. which was a bit too much. Now i split that into different controllers getting specific data. I guess that is the best way... i think your point number 2 is what should be done. – harikrish May 01 '14 at 17:28
  • 1
    I dont think this is the solution to what is asked, but an alternative. – tnchalise Aug 26 '15 at 08:30
7

I've been playing with the idea of bundling services based on controllers.

So in your example you'd refactor your; AppFactory, Menu, filterFilter and Notice services into a single service e.g. ViewAppsServices.

Then you'd use your services like ViewAppsServices.AppFactory.yourFunction().

As I see it that way you can at least shift your injections into another file cleaning your controller up a bit.

I think readability would suffer a bit since another developer would then have to look at bundles rather than the controller itself.

Here's a JSFiddle I put together to demonstrate how it would work; this is how I'd imagine yours would work.

.service('ViewAppsServices', ['AppFactory', 'Menu', 'filterFilter', 'Notice', 
function (AppFactory, Menu, filterFilter, Notice) {
    return {
        AppFactory: AppFactory,
        Menu: Menu,
        filterFilter: filterFilter,
        Notice: Notice
    };
} ])
aozd4v
  • 96
  • 1
  • 3
1

Try to move as much logic as possible to services, even just make controller methods act as "routing - passing through" methods . After time you will see it very usefull if you will want to use similar methods in other controllers/directives. Anyway, 7 injections is in my opinion not much :)

(edit: see the comment of Matt Way below) Also, a tip - in newer versions of Angular you don't have to write this array, just:

app.controller('viewapps', function($scope,Appfactory,Menu, $timeout,filterFilter,Notice,$routeParams){
   //controller code..    
}])
licancabur
  • 635
  • 5
  • 11
  • 6
    You have never had to, but the array is used so that you can do things like safely minify your angular code. It is best practice to always use the array labelling. – Matt Way Apr 30 '14 at 07:38
  • 1
    IMO the array notation is best left to a build step which can insert it. To me, best practice is to not repeat yourself, and leave that burden to the machines. See github.com/btford/ngmin – Jamie Pate Feb 27 '17 at 21:33
-3

My approach is to use $injector, when there are lots of dependencies:

app.controller('viewapps', ['$scope','$injector',function($scope,$injector){                               
    var Appfactory = $injector.get('Appfactory');
    var Menu = $injector.get('Menu'); 
    //etc...
}]);

The advantages:

  • Code can be minified and obfuscated safely
  • You don't need to count the index of the dependency, when you declare dependency as a function's parameter
Engineer
  • 47,849
  • 12
  • 88
  • 91
  • There are tools, like https://github.com/btford/ngmin that pre-minify Angular, so you can still write the functions the "nice" way, and have the code ultimately minified. Also, could this be essentially masking long parameter lists, which are usually recommended to be avoided? http://stackoverflow.com/a/175035/1319998 – Michal Charemza Apr 30 '14 at 08:04
  • @MichalCharemza Thanks for `ngmin` tool.I will research. About the masking, I can't really say, what really OP wants. Maybe the OP finds, that long list of parameters are not aesthetic at all ) – Engineer Apr 30 '14 at 08:08
  • 1
    -1 for the approach :) It beats one of the main benefits of DI. Each resource should be explicit about its dependencies. Using the `$injector` obfuscates the actual dependencies of the resource (in this case `controller`) and kills maintainability. (Beware of the pschycopath thatwill end up maintainng your code and knos where you live. He ain't be happy at all...) – gkalpak Apr 30 '14 at 08:52
  • @Engineer I think I assumed that the OP wanted a) judgement on his code structure based on the long argument list and b) suggestions to improve – Michal Charemza Apr 30 '14 at 08:55
  • @ExpertSystem The OP can write jsdocs about all the dependencies, so there will be no problems related to maintainability ,) – Engineer Apr 30 '14 at 09:04
  • @Engineer: Another -1 for proposing to write bad, obfuscated code with overly verbose documentation (the purpose of documentation is not to explain badly written code. Note that I did not mention the negative impact on testability (and thorough documentation won't help you with that). – gkalpak Apr 30 '14 at 10:07
  • @ExpertSystem I don't know,why you think the OP's code and/or my will be unmaintainable and/or will have *overly verbose* documentation.Please do not overstate everything. You have your opinion dude, that's ok ,) – Engineer Apr 30 '14 at 10:57
  • @Engineer: I didn't say anything about the OP. I commented on your suggestion to use `$injector` in order to "obfuscate" your controller's dependency and then use documentation to document what should be explicit in code in the first place. I know it's OK to have my opinion and that very opinion is what I expressed. (And yes, according to my opinion, having to document something that should be explicit in code is "overly verbose".) I believe comments are good for expressing opinions, so I think everything is OK :) – gkalpak Apr 30 '14 at 11:03
  • @ExpertSystem I have mentioned about the jsdocs according to your complainment about maintainability.I believe code can be structured in a such way, that the declaration and usage of *all* dependencies can be obvious and clear.So there will be no problems related to maintainance. Good luck ,) – Engineer Apr 30 '14 at 11:09
  • 6
    The given solution with the $injector is an implementation of the Service Locator pattern. [This is an anti-pattern](http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/) (in Javascript as well) and should be avoided. Besides, you now hide the real cause which is a [Single Responsibility Principle](https://en.wikipedia.org/wiki/Single_responsibility_principle) violation. – Steven Apr 30 '14 at 12:03
  • 1
    @Steven Your link about the anti-pattern is not applicable for the Javascript.In any case, browser interprets Javascript by *runtime*(there is no *compile-time* things),and it does not metter you inject the services as a function's parameter or by using `$injector`. It's almost the same.I assume, that you are not familiar with Javascript and especially with AngularJS. I am aware of Single Responsible Principle as well, but I don't know why do you mentioned about it in here. Your comment is very generic and out of the question's context. – Engineer Apr 30 '14 at 13:19
  • 1
    @Engineer I can see why the point on SRP would be in context: the long argument list, that the OP suggests will get even longer, suggests that the controller will be interacting with all of the injected dependencies. This in turn suggests the controller has many responsibilities. – Michal Charemza May 01 '14 at 14:23