Staeff Staeff - 1 month ago 7
AngularJS Question

Same "controller as" name in angular js directive breaks function in parent controller

I've a controller with some items for

ng-repeat
and each item should get a random color so I use
ng-style
with a function in that controller called
randColor(...)
.

app.controller('TestController', function() {
var vm = this;

vm.items = [ { name: 'item 1' } , { name: 'item 2'} ];

vm.randColor = function (item) {
if (!item) {
return 'red';
}
else if (!item.color)
{
var color = 'rgb('
+ _.random(0, 255) + ','
+ _.random(0, 255) + ','
+ _.random(0, 255) + ')';
item.color = color;
}

return item.color;
};
});


I'm using the "controller as" syntax for this and I usually always use
vm
as the short name of my controllers. I've never had a problem with doing so, even when naming "sub"-controllers the same way.

But now I've tried to do the same thing with a directive and suddenly my
randColor(...)
function stopped working.

Here is a plunker of my problem.

HTML:

<body ng-controller="TestController as vm">
<!-- This works -->
<h3>Without Directive (controllerAs: 'vm')</h3>
<div ng-repeat="item in vm.items">
<div ng-style="{ background: vm.randColor(item) }" class="container">
<h4>{{ item.name }}</h4>
<div ng-controller="TestDirectiveController as vm">
<div>{{ vm.title }}</div>
</div>
</div>
</div>

<!-- This works -->
<h3>Test Directive Alternative (controllerAs: 'directiveVM')</h3>
<div ng-repeat="item in vm.items">
<div ng-style="{ background: vm.randColor(item) }" class="container">
<h4>{{ item.name }}</h4>
<test-directive-alt></test-directive-alt>
</div>
</div>

<!-- This DOES NOT work -->
<h3>Test Directive (controllerAs: 'vm')</h3>
<div ng-repeat="item in vm.items">
<div ng-style="{ background: vm.randColor(item) }" class="container">
<h4>{{ item.name }}</h4>
<test-directive></test-directive>
</div>
</div>
</body>


JS:

app.controller('TestDirectiveController', function() {
var vm = this;

vm.title = 'test';
});

app.directive('testDirective', function() {
return {
restrict: 'EA',
controller: 'TestDirectiveController',
controllerAs: 'vm',
bindToController: true,
template: '<div>{{ vm.title }}</div>'
};
});

app.directive('testDirectiveAlt', function() {
return {
restrict: 'EA',
controller: 'TestDirectiveController',
controllerAs: 'directiveVM',
bindToController: true,
template: '<div>{{ directiveVM.title }}</div>'
};
});


Output:

enter image description here

I know I could just use a different name for the controller as in my example, but why does this happen in the first place?

And is there a way to get it working with the same name?

Answer

The problem you're facing seems to be related to the fact that the directive is being executed on the same scope as the scope where the controller is defined as vm.

What you need to do is to create a new scope scope: {} within the directive.

app.directive('testDirective', function() {
        return {
            restrict: 'EA',
            scope: {},
            controller: 'TestDirectiveController',
            controllerAs: 'vm',
            bindToController: true,
            template: '<div>{{ vm.title }}</div>'
        };
    });

With that, the controllerAs should create a new vm attribute in the directive scope.