Styleguide: controllers

Controller, scope, model and view – all those give us possibility to create chaotic mess. But it also gives us possibility to create beautiful and minimalistic harmony!

TL;DR


AngularJS apps architecture

In Model-View-Controller pattern… Oh, wait, is that really MVC in AngularJS?

I dare to argue, but that’s not exactly what this article is about. It does not matter if it’s kosher MVC, MVVM or MVP. Let’s use common MV* (Model-View-Whatever) label for AngularJS apps architectural pattern.

To make good* AngularJS app, we need to:

  • properly separate concerns
  • keep it data driven
  • take care of single responsibility principle

*good – maintainable, testable, readable, extendable, etc…


Separation of concerns

AngularJS decouples app into three zones:

  • Presentation (CSS)
  • Functionality (HTML)
    • basic html
    • extended html – directives
  • Logic (JS)
    • controllers (view logic)
    • services (business logic)

Conclusions:

  • do not manipulate DOM or styles in controller (css and directives are for that)
  • do not calculate data in controllers (services should do that – use rich object model pattern or anemic model and helpers)
  • do not communicate with back-end in controllers (create communication layer services)

Data driven application

Application state is just a compound state that incorporates states of all models currently represented in view and UI states. UI state informs us which models to show in current view what view template to use, but all the structure and fragmentary view states are one-2-one representation of underlying data.

View is a representation of the model.

Storing too much information of current state in abstract memory can:

  • easily lead to hard to track bugs
  • complicate application structure
  • impact maintenance

Conclusions:

  • do not store app state info in view (css classes, element data) but in service or view-model
  • do not store app state info in solitary variables – always keep it in some higher level structure (service, controller)

Single Responsibility Principle

In object-oriented programming, the single responsibility principle states that every class should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class.wikipedia

Among AngularJS controller’s responsibilities we can list:

  • communication between view and model
  • view-model definition
  • view logic

Things that are NOT there are:

  • business logic (use a service)
  • communication with API (use a service)
  • application state control (use a service)
  • direct view modification (use a directive)
  • and many more…

AngularJS 1.x messes some things up a bit. It provides $scope which is a ViewModel from MVVM and Controller that represents C in MVC. After testing several approaches I found out one that seems to be the most minimalistic, clean and readable – treat Controller as ViewModel and explicitly use $scope only if Controller has to watch some model values (optimization) or listen to events (probably only '$destroy').

Conclusions:

  • assign model entities directly to Controller not to $scope
  • assign any view control fields directly to Controller not to $scope
  • use $scope only for watches and event listeners
  • do not encapsulate application state logic in any Controller (use a routing service)

The Style Guide

To sum up there are three areas you should keep in mind when implementing a well styled AngularJS controller. Separate concerns, give things single responsibility and make your app data driven. To achieve this follow few simple principles:

Recomended:

  • assign model entities directly to controller not to $scope
  • assign any view control fields or methods directly to controller not to $scope
  • use $scope only for watches and event listeners (only if it’s indispensable)

Caution:

  • do not store view state info in view (css classes, element data) but in service or controller
  • do not store app state info in solitary variables – always keep it in some higher level structure (service, controller)
  • do not encapsulate application state logic in any controller (use a routing service)
  • do not calculate data in controllers (services should do that)
  • do not communicate with back-end in controllers (create communication layer services)
  • do not manipulate DOM or styles in controller (css and directives are for that)

These rules are outcome of almost 3 years of my work with AngularJS but there are some different approaches out there in the internet. Just pick one and follow – avoid chaos.

“All things are lawful,” but not all things are helpful.
“All things are lawful,” but not all things build up. 1 Corinthians 10:23


Further reading