Do You Like Your Angular Controllers with or without Sugar? | John Papa

John Papa

Evangelist on the loose

Do You Like Your Angular Controllers with or without Sugar?

...

sugar-make-us-age-1Even if you’ve only read about Angular, the odds are you’ve seen the rampant use of $scope in the C of MVC (controllers). $scope is the glue between the Controller and the View that helps with all of our data binding needs. Recently the Angular team opened up a new way to use $scope with Controllers. So now you can use $scope (what I’ll refer to as Classic Controllers) and you can use this (what the Angular team and I refer to as Controller As). I hear a lot of questions about these 2 techniques. Everyone loves choice, but at the same time, most folks like to know clearly what they are getting or giving up with the choices. So let’s discuss the two controller constructs in Angular (with $scope and Controller As) and how scope plays in both of these.

Both Classic Controller and Controller As have $scope. That’s super important to understand. You are not giving up any goodness with either approach. Really. Both have their uses.

First, some history …

$scope is the “classic” technique while “controller as” is much more recent (as of version 1.2.0 officially though it id appear in unstable pre-releases prior to this). Both work perfectly well and the best guidance I can give is to try to be consistent with choosing one or the other. You can mix them in the same app, but for Pete’s sake have an explicit reason for it first. So pick one and roll with it. The most important thing is to be consistent. Which one? That depends on you. There are many more examples out there of $scope, but “controller as” is picking up steam as well. Is one better than the other? That’s debatable. So how do you choose?

Comfort I prefer the “controller as” because I like hiding the

$scope and exposing the members from the controller to the view via an intermediary object. By setting this.*, I can expose just what I want to expose from the controller to the view. You can do that with $scope too, I just prefer to use standard JavaScript for this. Overall, for me it really just comes down to personal preference and mine is that I prefer the Controller As technique. In fact, I code it like this:

var vm = this;

vm.title = 'some title';
vm.saveData = function(){ ... } ;

This feels cleaner to me and makes it easy to see what is being exposed to the view. Notice I name the variable “vm” , which stands for viewmodel. That’s just my convention. With $scope I can do the same things, so I’m not adding or detracting with the technique.

$scope.title = 'some title';
$scope.saveData = function() { ... };

So its up to you there.

Injection With

$scope I do need to inject $scope into the controller. I don’t have to do this with controller as, unless I need it for some other reason (like $broadcast or watches, though I try to avoid watches in the controller). This is another reason I prefer Controller As: I like knowing that I only inject $scope explicitly if I need something besides data binding. Listening for a broadcast message is one example. A watch is yet another, though I try to avoid watches in controllers.

Trends There appears to, at this time, be more code examples out there using the classic approach with $scope explicitly. However I am seeing more and more examples of Controller As. If you want a file template for creating controllers you can use

SideWaffle, a plug-in for Visual Studio. It offers both flavors of controllers in its file templates. Don’t like sugar? choose classic controllers with $scope. Want some sugar? Choose controller as. The Angular team has given us options and I’m glad they have. I personally prefer the Controller As technique. Either way you get data binding. With Controller As you get some sugar on top that makes working with $scope feel better, in my opinion. So you just have to choose if you want your Angular with or without sugar :)

tags: angular patterns
  • Joe Beernink

    I just picked up Angular (watched a lot of Pluralsight over the weekend, thank you very much). With no historical context, I greatly prefer the Controller As syntax. While I’m $scope works, the Controller As seems much clearer to me.

    On the flip side of things, I hadn’t worked in ASP.NET MVC since MVC3, and the whole idea that the client side now has a controller (which is actually a view model), blew me away, and seems to be a bit of a misnomer. It didn’t take too long to get used to the new terminology, but it was a bit of a shock to my outdated neurons.

  • Jesse Liberty

    John,

    You are not only a font, you provide a critical service to the community. Great article, thanks!

    -jesse

  • Daniel Root

    One possible +1 for the “Controller As” method is that done a certain way it can be made to port to other frameworks. I recently did a grid that worked both on Angular and on Knockout. On Angular, I just bound to the viewmodel. On Knockout I had to use KO mapping first to wrap the model, and provide a shim for $http. NBD if you’ve chosen a framework, but if you’re writing something to plug into other frameworks, it can be a bonus.

  • Sk.Tajbir

    very helpful. Thanks for sharing.

  • Malik Berger

    watching your plural sight video now. This posts helps clarify the $scope since you aren’t using it in your video. Thanks for all the hard work!!!

  • http://www.greglockwood.com/ Greg Lockwood

    Why do you avoid watches in your controllers? Isn’t that one of the primary ways you observe changes to properties? In particular, I find using watches to synchronise, say, a DB representation of an object and an in-page representation of the data, very helpful.

    We have even been able to use watches as a way of simulating Knockout’s “computedObservable()”, by dynamically registering watches on dependencies and re-running the function to generate the computed property if any of them change. What is your substitute for this kind of task?

    • johnpapa7

      Greg – I generally can find alternatives to putting watches in my controllers. For example, most things I want to watch are already data-bound in my view. So if an array changes that is bound to an ng-repeat, instead of a watch I just run my code wherever the array changes in the controller.

      For computed/calculated properties I often use ES5 object.defineProperty (getter/setter).

      I prefer to keep watches out if I have alternatives means. This also usually means I can eliminate the need for an “apply” in my test too.

  • Martijn Boland

    I’m missing one of the big pro’s of $scope: you can simply use it in async event handlers. With ‘this’, you always have to introduce extra variables (var self = this) to be sure you’re using the same ‘this’.

  • Joshua Bowdoin

    I notice that you do:

    vm.myFunction = myFunction;

    function myFunction(){
    //code here
    }

    Is this just style? or are there benefits to your approach versus just doing:

    vm.myFunction = function (){
    //code here
    }

    • johnpapa7

      Joshua – it is purely style on my part. I prefer to separate the function definition from where I expose it so I can easily see all exposed functions at the top. If my function is a 1 liner, I often leave it up top tho. If its not, then I separate it. Otherwise, I feel the code is harder to read when i am trying to find what features are exposed on the scope.

      • Joshua Bowdoin

        Thanks!

  • David Chase

    I personally prefer “controller as” syntax because when i have nested controllers I like to see what belongs to each controller..

    Thanks for the great article

  • http://vkelman-blog.blogspot.com/ vkelman

    There was an interesting discussion on $scope vs controller as here:
    https://groups.google.com/forum/m/#!topic/angular/84selECbp1I

  • James

    John,

    I’m just getting started with Angular and am using the SideWaffle templates as I build out the application. Because I’m a noob (both in Angular and Javascript) some things are entirely clear and I was wondering if there was a “best practices” site or somewhere I could learn where to store intermediate variables that I didn’t want to expose to users of the controller, but wanted to be initialized with it (and not in each call). For instance, I’ve got a search service defined (using the amazing Algolia) and as the service template also follows the “expose what you intend, hide everything else” pattern I wasn’t sure where to store the client variable the service uses to communicate with its server. Do I just declare them in the service/controller function scope or is there a better method?

    Thanks so much!

    James

    My Service (such as it is):

    (function () {

    ‘use strict';

    // Factory name is handy for logging

    var serviceId = ‘algolia';

    // Define the factory on the module.

    // Inject the dependencies.

    // Point to the factory definition function.

    angular.module(‘searchApp’).factory(serviceId, [algoliaService]);

    function algoliaService() {

    // Define the functions and properties to reveal.

    var service = {

    search: search

    };

    var algolia = new AlgoliaSearch(“myappkey”, “myapikey”);
    var index = algolia.initIndex(‘gear’);

    return service;

    function search(query, callback) {

    index.search(query, callback);

    }

    //#region Internal Methods

    //#endregion

    }

    })();

    • johnpapa7

      Variables declared in the service won’t be exposed unless you return them as part of the returned object. Or you can make your key a constant with angular. Or you can expose a series of constants from a confit service,which is what I often do. See my code from my course on that.

      Hope this helps

  • pupuzuken

    If I’m using some open source controllers I have found for certain functions, can these be integrated easily into Hot Towel? Or will I need to edit them to use vm instead of $scope?

    • johnpapa7

      you can use $scope and vm techniques mixed, tho I personally prefer to choose one.

  • David Land

    You’re missing the main feature of “controller as” which is to allow you to easily nest controllers and cleanly refer to the proper one inside your html.

    http://toddmotto.com/digging-into-angulars-controller-as-syntax

    {{ main.title }}

    {{ another.title }}

    {{ yet.title }}

  • Samuel Durand

    Hi, I like this “controller as” idea, like you said it is much more clear especially with nested controllers. However I often can’t use it when I want to do unit testing or use watchers for the simple reason that I never manage to find the value by looking in the $scope. I simply don’t know where to find the value if I use this.value .
    A simple example :
    $scope.$watch(‘data.selectedNode.id’, function() {} .
    This code is fine if I did $scope.data before. But if I did this.data{…} then I don’t know how to call the value.

    • johnpapa7

      You are not restricted by controllerAs. You can achieve a watch using syntax similar to this:

      $scope.$watch(‘vm.title’, function(current, original) {

      $log.info(‘vm.title was %s’, original);

      $log.info(‘vm.title is now %s’, current);

      });

  • New Dev

    John, can you clarify how scope inheritance works with Controller-As? The alias assigned to the parent controller now requires the child controller to use $scope.parentAlias to access its scope, but the alias is defined by the view. This tightly ties the controller with the view.

  • Daryl Napp

    Hey man you should check this article again, I think you messed up some of the headers for your sections.

  • Colin Hughes

    I’ve been getting to grips with HotTowel via your PluralSight course and I just used some of it to convert some old jQuery code to AngularJS. I had to convert the controller to use $scope in the end because I wanted to ensure an $interval was cancelled in the $destroy, but ControllerAs wouldn’t allow me to hook $destroy up (I got a js error). With this minimal information to go on, do you know why this might be?

    • Colin Hughes

      I’ve been doing lots of reading around this since I posted the comment above yesterday, and I am starting to realise that, certainly in the case I encountered this (but probably this is a general rule) I shouldn’t be using $interval in the Controller – I think I should move it to the directive. Reading this article ( http://toddmotto.com/rethinking-angular-js-controllers/ ) suggests that business logic should never be in the controller, so maybe I would never need $destroy in the controller, but I suspect I might, so I am still interested in your thoughts…

%d bloggers like this: