Ryan Langton
Ryan Langton

Reputation: 6160

Using ControllerAs with a Directive

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";
}

Upvotes: 28

Views: 35339

Answers (5)

Paul Fox
Paul Fox

Reputation: 1

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>

Upvotes: 0

jitendra varshney
jitendra varshney

Reputation: 3562

When you use controllerAs syntax, then you have to use

bindToController: true

it will work in your directive.

Upvotes: 1

Amardeep Kohli
Amardeep Kohli

Reputation: 502

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>'

Upvotes: 1

Tr1stan
Tr1stan

Reputation: 2775

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

Upvotes: 39

New Dev
New Dev

Reputation: 49590

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".

Upvotes: 4

Related Questions