ng-controller core directive is applied to a containing element within your app’s HTML, and acts to assign a scope (i.e. an execution context) that’s limited to that element and its children. Declare an
ng-controller directive on a
div element, for example, and the scope of that controller is limited to just that
div element and its children.
But although componentizing your HTML page into discrete modules is good practice, using
ng-controller to do it isn’t. With many
ng-controller directives declared across your HTML, you’ll find it difficult to work out what controller is acting on what part of the page, as the only way to tell is to trawl through reams of generally poorly-formatted HTML.
Worse, nested controllers inherit their parents’ scope, but you may not be aware that you’ve actually done this. The parent scope’s properties are thus available to the child scope’s, which has the same bad effects as creating globals, as you’re free to mutate and even override the parent controller’s properties at will.
controllerAs syntax can help, but it’s still hard to parse the exact scope of a controller if it’s buried within the HTML.
Equally, having one giant ‘root’ controller is also poor form, as the code within the controller will quickly become unmanageable and untestable.
But all that said, you still need to break your page up into discrete sections, each of which contains its own scope and logic. So how do you do that?
Simple – your own custom directives! A directive is bound to its own controller with its own scope (non-inherited if you use isolate-scope, which you should), and which shares state across other directives via services injected into the directive’s controller.
At a simple stroke, you have an easily identified execution context that’s bound only to the directive and its children. The controller is easy to identify, as it’s declared within the directive’s definition object, and any business logic the directive needs to execute is contained within services that are injected into its controller.
The directive thus becomes an easily-testable, modular, reusable component that can be used as is one any other within your app (or indeed, across other apps).
And if you need to use a ‘root’ controller for your page (for initialisation, for example), then bind it to the page’s url and
ng-view directive using the
$routeProvider service – again, no need- for
AngularJS’s docs really do need a section that says “warning, for small, tiny apps only!”