Angular's "controllerAs" not working in routeProvider

thanpolas picture thanpolas · Aug 14, 2014 · Viewed 13.3k times · Source

I am trying to use the controllerAs property on a $routeProvider route without any success.

Here is the sample code:

var app = angular.module('app', ['ngRoute']);

app.config(['$routeProvider', '$locationProvider',
  function($routeProvider) {
  $routeProvider
    .when('/', {
      template:'<div>This should be visible:{{ ctrl.one }}</div><div>This should not:{{ one }}</div>',
      controller: 'Ctrl',
      controllerAs: 'ctrl',
    });
}]);

app.controller('Ctrl', function($scope) {
  $scope.one = 'actual';
});

Not sure if this is a bug or I am doing something wrong, This is a plnkr that demonstrates the issue

Answer

Rohan B&#252;chner picture Rohan Büchner · Aug 14, 2014

Actual problem:

You seem to be using controllerAs (assigning a value of 'ctrl') originally, but then later not making use of it in the rest of your code. (you used $scope)

enter image description here

Solution:

Working demo as per your sample

When I've been using the controllerAs syntax you need to use one of the following pattern(s) to get access to the instance of the controller:

As opposed to appending the property to $scope, you have to bind to the controller scope. Note this is different to $scope. For lack of a better term, you need to bind controller itself (think of it as its context). As we're dealing with the display layer or view model, I tend to use var vm = this; as a convention, but this personal preference.

[A]: Preferred Solution

app.controller('Ctrl', function() {        
    this.one = 'actual';  
});

//or using 'vm' convention

app.controller('Ctrl', function() {
    var vm = this;
    vm.one = 'actual';  
});

[B]

app.controller('Ctrl', function() {
    var vm = {};
    vm.one = 'actual';
    return vm;
});

Explanation:

When I first started using Angular, [B] was the approach that I used, purely coming from a Knockout background. I was used to binding to a "container" object then binding the object to the view. That being said, I prefer to use [A], or append directly to $scope and forgo the alias completely. Reasons:

  • I feel its cleaner ITO readability
  • As @Swordfish0321 stated, [A] is more performant (should it be a concern to you)
  • I had binding issues with custom directives I wrote that where dependent on certain parent scope properties (specific to my code-base)

Just as a visual:

Demo

 app.controller('Ctrl', function($scope) {
      var vm = this;
      vm.one = 'actual'; 
      console.log($scope) 
 });

Passing in the $scope object and further inspecting it, you'll see a new ctrl child object containing all your public properties and functions that was bound to vm inside the controller code. This is because you've assigned var vm = this. Meaning the vm object in the code is referencing the controller's own scope, which ultimately gets bound to the view. controllerAs basically groups all properties and functions contained internal to the controller to a new object named after the alias that you've provided.

enter image description here