Thoughts on Angular's Controller As Syntax

Monday, August 11, 2014

I’ve been a bit late to the Angular controller as syntax. I was skeptical of the feature at first, and with an Angular project already in flight, I felt it wasn’t the type of change to make with a significant amount of code already committed.

The controller as syntax allows me to alias a controller in markup.

<div ng-controller="MainController as main">
    {{ main.title }}

Then instead of injecting a $scope into the controller, model data and behavior is added to the controller instance itself.

app.controller("MainController", function(){
    this.title = “Hello!”;

Behind the scenes the controller as syntax adds the controller alias (main in the above example) to a scope object, because Angular still evaluates all binding expressions against a scope object.

Here’s what I think about controller as now.

The Good

- Using the controller alias in a view is a push into the pit of success. View code is more explicit and easier to maintain. Even when using $scope in a controller, I’ve come to view any reliance on prototypal inheritance in the scope chain with suspicion, as the inheritance is brittle, subtle, and often complicates both controller and test code. With a controller alias there is no reliance on scope inheritance hierarchies.

- Not having to inject a $scope makes test code slightly easier.

- Having no access to the $scope API inside a controller is a good thing. I’ve come to view any use of $scope.$watch, $scope.$emit, $scope.$on, and $scope.$* in general with suspicion, at least when inside the controller code for a view. Nearly all the functionality available through the $scope API is better used inside of directives or services that provide a better abstraction.

- Thinking of the controller as a true constructor function instead of a function that adds stuff to $scope is healthier. You can take advantage of the constructor function prototype property to organize code, and this will work well with ES6 class definitions (which presumably will work well with ng 2.0). 

The Bad

- Writing directive controllers is different than writing view controllers. With directive controllers the instance members are often used as an API for intra-controller coordination, and this API should be separate from the model for the view.

- Somewhat related to the above, I’m still not convinced that tangling the controller and view model together is a good idea. I think there is some value in having a dedicated factory for view models.


The good appears to outweigh the bad, so I’ll switch over to using controller as syntax moving forward. I don’t think controller as is a panacea for complicated$scope code, but it does push people in a better direction.

I also think controller as  highlights the need for Angular 2.0 to make some huge, breaking changes. Once of Angular’s greatest strengths, for me, is that the framework provides enough flexibility to build a variety of apps both large and small. Routing is optional, for example, and most programming can be done with plain old JavaScript objects. However, the surface area of the framework is dangerously close to being a Mirkwood forest of programming techniques. A streamlined API with stronger opinions about how to tie components together would make the framework easier to use.

gravatar Rob Monday, August 11, 2014
Currently, Angular 2.0 has no "scope" object at all. Instead, every view has an execution context that it binds against. There is no prototypal inheritance or any of the strange scope pieces anymore. Essentially, it's like "controller as" baked in. It is a breaking change, but it gets all the advantages as you mention above. Also in Angular 2.0, there is no difference in the way you build a Controller/View pair vs. building a directive..they are the same thing now. We've significantly simplified "component" or "element" directives so that they are really easy to make. There's no compile, link, etc. You just create a class, add some annotations and you are done. (And if I get my way, you won't even have to add the annotations for the controller/view scenario.) It's all still a work in progress and subject to change, but I think we are heading in a direction that you will like.
gravatar David Boike Monday, August 11, 2014
Agreed on all points. Like you, I just finished a project where switching to "controller as" midflight would have been irresponsible. Curious on your thoughts about how "controller as" affects managing "this". One thing I liked about $scope (aside from the detriments) was that having $scope available in closure scope prevented having to muck around with that/me/etc. Granted a callback that needed "this" could be a code smell that you should be in a directive at that point anyway, but I'd like to hear your thoughts all the same.
gravatar scott Monday, August 11, 2014
@Rob: Good to hear! @David: yes, that was my initial thought, too. I dislike having "var self=this;" statements in the code, but it is still the best solution, I think, at least until we are all using ES6 arrow functions.
gravatar Osmyn Monday, August 11, 2014
As someone who is just learning Angular, it's good to see you evaluate the differences. I was tending towards the controller-as mode but after watching your Pluralsight videos and webinar kept questioning if I was missing something since you were sticking to the scope mode. I second your call for a more opinionated surface area.
gravatar Prasanna Pattam Wednesday, August 13, 2014
I like the controllerAs functionality. With this Angular controller looks similar to the Durandal's ViewModel. If we can use the Revealing Module pattern, then we can get away from "this". I blogged about this last week.
gravatar scott Wednesday, August 13, 2014
Prasanna, yes that works, too.
gravatar sreekanth Thursday, August 14, 2014
whta is diff between controller and controllerAs....
gravatar Adrian Hara Friday, August 15, 2014
We're using view models, that is, we're creating end exposing view models on the $scope from the controllers. The controllers are almost devoid of functionality, their job is to instantiate the view model correctly and expose it. The view models are declared using .factory(). It's been working out pretty well so far in a medium-size project and we've had absolutely no issues due to scope inheritance problems.
gravatar Dave Thursday, September 11, 2014
Thanks for your thought. I have become fond of using controller as in my controllers for much the same reasons you've listed above, and have also found when using $watch to watch parent scope, the need to use angular.bind to explicitly watch parent scope items make code even more explicit. My challenge has been in applying controller as in directives with islated scope. although i'm still in my initial experiments, it appears to me that isolated scope items that are injected into the directive still need to be access on $scope which start to make the directive read weirdly. i was wondering if you could share your thoughts on this.
gravatar Scott Thursday, September 11, 2014
Dave: Yes, programming with directives now becomes an entirely different world, which is unfortunate. I don't see a way to resolve the two different programming models currently, at least not without changing Angular itself.
Your Comment
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!