Using ControllerAs with a Directive

35.2k Views Asked by At

I'm trying to follow John Papa's angularJS style guide here and have started switching my directives to using controllerAs. However, this is not working. My template cannot seem to access anything assigned to vm. See this very simple plnkr example that exhibits the behavior.

http://plnkr.co/edit/bVl1TcxlZLZ7oPCbk8sk?p=preview

angular
    .module('app', []);

angular
    .module('app')
    .directive('test', test);

function test() {
    return {
        restrict: 'E',
        template: '<button ng-click="click">{{text}}</button>',
        controller: testCtrl,
        controllerAs: 'vm'
    }
}

angular
    .module('app')
    .controller('testCtrl', testCtrl);

function testCtrl() {
  var vm = this;
  vm.text = "TEST";
}
5

There are 5 best solutions below

2
On BEST ANSWER

When using the controllerAs syntax you don't access the $scope as you would normally, the variable vm is added to the scope, so your button needs to become:

<button ng-click="click">{{vm.text}}</button>

Notice the vm. added to the beginning of text.

Here is a fork of your Plunk with the fix applied


Q: Do you know how I would access attributes passed through as attributes to the directive, example "scope: { text: '@' }"? Am I then forced to use $scope on the controller and set vm.text = $scope.text?

A: In the article you reference, there is a section y075 that talks about just this scenario. Look into bindToController:

return {
    restrict: 'E',
    template: '<button ng-click="click">{{text}}</button>',
    controller: testCtrl,
    controllerAs: 'vm',
    scope: {
        text: '@'
    },
    bindToController: true // because the scope is isolated
};

Then you should be able to access vm.text

0
On

With "controllerAs", the controller instance alias - vm, in your case - is published on the scope as the .vm property of the scope.

So, to access its properties (i.e. the properties of the controller), you need to specify {{vm.text}} or ng-click="vm.click".

0
On

When you use controllerAs syntax, then you have to use

bindToController: true

it will work in your directive.

0
On

I realise this ticket is quite old. I'm adding my $0.02 in case anyone stumbles on to this in the future.

Firstly, it would be nice to have some context around what you're trying to achieve, because you seem to be breaking design rules. Your directives shouldn't need to know the inner workings of your controller, or vice-versa.

I have created a simple example of how to set the caption of button in a directive. There is no need for a controller here, and I think it just makes your example difficult to follow.

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

myApp.directive('myDirective', function() {
  return {
    scope: {
      caption: "@"
    },    
    template: '<button>{{caption}}</button>'
  };
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>

<div ng-app="myApp">
  <my-directive caption="Hello World" />
</div>

0
On

When using 'controllerAs' syntax ,as above,the scope is bound to the controller’s 'this' reference. So it allows us to introduce a new namespace('vm' here) bound to our controller without the need to put scope properties in an additional object literal(say $scope). So accessing anything in controller's scope,requires 'vm' namespace, as,

'<button ng-click="click">{{vm.text}}</button>'