Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS: Why not write logic in controller?

Pardon me if this sounds stupid but I have been using AngularJS for a while now and everywhere I have seen people telling me to wrap my logic in a directive(or service ?) instead of my controller and keep only the bindings in my controller. Apart from the reusability aspect of a directive is there any other reason ?

Until now I haven't actually understood why this is the case. Doesn't writing a directive come with a lot of overhead ? I haven't faced any kind of problems writing logic in my controller and it is EASY. What ARE the downfalls of this approach ?

like image 834
Tarun Dugar Avatar asked Jul 16 '15 12:07

Tarun Dugar


2 Answers

The controller is the right place to do all and everything that is related to the scope. It is the place where you write all the

$scope.$watch(...)

and define all the $scope functions that you need to access from your views ( like event handlers ). Generally, the event handler is a plan function which will in turn call a function a service.

$scope.onLoginButtonClick = function(){
    AuthenticationService.login($scope.username,
        $scope.password);
};

On very rare occasions you can add a promise success handler in there.

DONT: Write business logic in controllers

There was a very specific reason why the earlier example was like that. It showed you a $scope function that was in turn calling a function in a service. The controller is not responsible for the login mechanism or how login happens. If you write this code in a service, you are decoupling the service from the controller which means anywhere else that you want to use the same service, all that you need to do is, inject and fire away the function.

Rules for the Controller going forward:

  • Controllers should hold zero logic Controllers should bind references to Models only (and call methods returned from promises)
  • Controllers only bring logic together
  • Controller drives Model changes, and View changes. Keyword; drives, not creates/persists, it triggers them!
  • Delegate updating of logic inside Factories, don't resolve data inside a Controller, only update the Controller's value with updated Factory logic, this avoids repeated code across Controllers as well as Factory tests made easier
  • Keep things simple, I prefer XXXXCtrl and XXXXFactory, I know exactly what the two do, we don't need fancy names for things
  • Keep method/prop names consistent across shared methods, such as this.something = MyFactory.something; otherwise it becomes confusing
  • Factories hold the Model, change, get, update, and persist the Model changes
  • Think about the Factory as an Object that you need to persist, rather than persisting inside a Controller
  • Talk to other Factories inside your Factory, keep them out the Controller (things like success/error handling)
  • Try to avoid injecting $scope into Controllers, generally there are better ways to do what you need, such as avoiding $scope.$watch()
like image 184
ngLover Avatar answered Sep 27 '22 17:09

ngLover


There two good reasons for me for keeping logic out of a controller:

Reusability

If your application has multiple controllers and each do pretty much the same thing with some differences then keeping logic in the controller means you will be repeating the code you write. It's better if you Don't Repeat Yourself. By putting that logic into a service you can inject the same code into multiple controllers. Each service (really a Factory) is created as a new instance of itself each time it is injected into a controller. By pushing logic into a service you can modularise your code, which keeps it easier to maintain and test (see below)

Testing

Good code is tested. Not just by people but by the unit tests you write. Unit tests give you as a developer assurance that your code does what you expect it too. They also help you design your code well.

If your controller has 20 different methods each with their own logic, then testing (and your code) is turning into spaghetti.

It's easier to write unit tests that are narrow i.e. they test one thing at a time. And fortunately it's also good (for the reasons outlined above) to break your code up into encapsulated pieces i.e. they do one thing and can do it in isolation. So unit tests (especially if you write your tests first) force you into thinking about how to break up your code into maintainable pieces, which leaves your application in a good state if you want to make changes in the future (you run the unit tests and can see where things break).

Example

Form application:

You have a form application serving multiple forms. You have a controller for each form. When the user submits the form the data is sent via a proxy to a CRM that stores the information in a database.

If a customer already exists in the CRM you don't want to create duplicates (yes the CRM should handle data cleansing but you want to avoid that where possible). So once the user submits their form data something needs to implement logic that goes something like:

  • search for the user in the CRM via an API endpoint
  • if the user exists get the user ID and pass it with the form data to another endpoint
  • if they don't exist hit another endpoint and create a new user, get the user ID and send it and the form data to associate it with the user

NB: Arguably all of the above should be done by a back-end service but for the sake of example let's go with it.

Your application has multiple forms. If you hardcode the same logic in each controller for each form (yes you should have a controller per form i.e. one per view) then you are repeating yourself multiple times. And writing tests that need to check the controller can do the basics (post data, manage changes to the view) but also test all of that of that logic for each controller.

Or instead write that logic once, put it in a service, write one test for it and inject it wherever you like.

References

Look at the Angular documentation, and look at the patterns that Angular implements and why these are good to follow (Design Patterns - the big ones being modular, dependency injection, factory and singleton).

like image 29
br3w5 Avatar answered Sep 27 '22 19:09

br3w5