2

I decided to put this question up because I got to a point where I'm pretty confused about how Angular and the more delicate parts of JavaScript work. I try to formulate my question so it can be broken down.

  1. The standard way of exposing variables from a controller is to declare them on $scope.

    .controller('MyCtrl', function($scope) {
      $scope.value = 5; 
    });
    

    Then from a template reference it like this, assuming MyCtrl is the controller:

    {{value}}
    

    When I use the controllerAs syntax, for example with ui-router like this:

    state('mystate', {
      templateUrl: 'partials/some.html',
      controller:  'MyCtrl as ctrl' }
    

    I can still get value with {{value}} so this works. However if I declare another value like this:

    .controller('MyCtrl', function($scope) {
      $scope.value = 5;
      this.otherValue = 10;
    });
    

    Then with controller: 'MyCtrl' syntax, I can not reference {{otherValue}}. However with the controller: 'MyCtrl as ctrl' syntax, I can reference them like this:

    {{value}} {{ctrl.otherValue}}
    

    Summary of point 1: I can always reference value like {{value}} but can only reference otherValue if the controller is named, so {{ctrl.otherValue}}.

  2. My second point is about functions and the topic is similar to point 1. Suppose I define two functions:

    .controller('MyCtrl', function($scope) {
      $scope.myFunc = function() {};
      this.otherFunc = function () {};
    });
    

    Now if again as above I use the controller: 'MyCtrl' syntax, from the template I can call myFunc:

    <button ng-click="myFunc()">
    

    But this will not work:

    <button ng-click="otherFunc()">
    

    Now with controller: 'MyCtrl as ctrl' syntax, calling myFunc like these will not work:

    <button ng-click="ctrl.myFunc()">
    <button ng-click="myFunc()">
    

    But I can call otherFunc by referencing the controller with the name:

    <button ng-click="ctrl.otherFunc()">
    

    Summary of point 2: I can only call $scope. functions like I'm used to when I don't use the controllerAs syntax, and can only call this. functions when I reference the named controller. This is in contrast with point 1 where I can reference the $scope. variable from either named or unnamed controller definition.

I know controller constructor functions are for setting up the $scope, and templates should use the $scope, but what is different about named controllers then? I'm guessing the controllerAs syntax causes that custom name to point to not the scope but the controller function itself, but I would be grateful if somebody would clear this up for me.

I also think these behaviours are not related to just ui-router but it's generally how it works, so I would also like to know:

  • What should be set on $scope and what shouldn't?
  • What should be set on the function itself(?) and what shouldn't?
  • Is there a way to access $scope. functions when using named controllers?
  • Any advice or some obvious point I'm missing?

Thank you for your time in advance.

tamacun
  • 421
  • 1
  • 4
  • 9

1 Answers1

1

The $scope and the controller are two different objects. (The controller function in Angular is actually a Javascript constructor for an object, and that object is accessible as normal using this.) So putting something in $scope - $scope.value = "xxx" - is totally different that putting something in this - this.otherValue = "yyy". The rule in Angular is that the $scope is what is accessible from view templates (i.e. the HTML), so anything under $scope is directly accessible with the {{value}} notation and template expressions.

To summarize: You cannot access members of the controller from the template just as you cannot access members of the window object (or the $http service, or $q, ..., and so on) because these are totally different objects than the $scope. Only members of the $scope are accessible from the view.

Naming the controller does a simple thing: the controller is placed in the $scope under the given name. Therefore the controller is accessible e.g. as {{ctrl}}, and its members accessible as {{ctrl.otherValue}}. (To make certain things are clear: executing this.otherValue = "yyy" in the controller makes otherValue a member of the controller.)

I hope this answers questions (1) and (2). For the rest:

What should be set on $scope and what shouldn't?

Place in the $scope things that need to be accessible from the view.

What should be set on the function itself(?) and what shouldn't?

I'm not sure what you mean by that. If you mean this.xxx = "yyy" in the controller, this does not place xxx in the function, rather in the object constructed by the function (this).

Is there a way to access $scope. functions when using named controllers?

You can always access $scope members (functions or not). Don't get confused: In your example above, i.e. $scope.myFunc = function() {}; this.otherFunc = function () {};, myFunc is a member of the $scope object, not the controller, so there is no way to access it as ctrl.myFunc().

Any advice or some obvious point I'm missing?

It is not clear in simple cases that the controller is actually a constructor function called with new and creates a new object. This is really useful in cooperating directives, where one directive can require the controller of another directive it cooperates with and call controller member functions.

Nikos Paraskevopoulos
  • 39,514
  • 12
  • 85
  • 90
  • 1
    To add to this, my rule of thumb is to put *data* on `$scope` and *behavior* on the controller. – Jeff Hubbard Dec 09 '13 at 01:16
  • Thanks @Nikos, I probably was half-aware of this, just got confused because I didn't know for sure. Your answer enlightened me in a snap. I've also found [this great answer](http://stackoverflow.com/questions/11605917/this-vs-scope-in-angularjs-controllers/14168699#14168699). @Jeff This is exactly what I came up with, just feels better structured somehow. Based on the linked answer one just needs to be careful not to put `this` into functions defined on `$scope` because the closure won't work as you'd expect. – tamacun Dec 09 '13 at 08:24
  • @JeffHubbard Yes, this really sums thing up well! – Nikos Paraskevopoulos Dec 09 '13 at 08:35