I have been learning about the kind of "new" "Controller as" syntax. While I find that the syntax is clearer for readability sometimes to do a relative simple thing it just gets more complicated, for example when adding a Directive to a Controller.
How would this simple sample be done with the "Controller As" syntax?
Plunk Sample
I tried something like this:
app.directive('myCustomer', myCustomer);
function myCustomer() {
return {
restrict: 'E',
scope:{ customer: '=info'},
//templateUrl: 'my-customer.html',
template:'Name: {{vm.customer.name}} Address: {{vm.customer.address}}',
controller: Controller,
controllerAs: 'vm',
bindToController: true
};
}
I don't quite get it to work just as the regular "$scope" syntax. Maybe I am missing something.
Note: The sample uses Angular 1.5.5
Check this fork of your plunk: https://plnkr.co/edit/7iA3JMhuUlvIQN9ORs81?p=preview
.directive('myCustomer', function() {
return {
restrict: 'E',
scope: {
customerInfo: '=info'
},
controllerAs: 'vm',
controller: ['$scope', function(scope){
console.log(scope.customerInfo);
}],
templateUrl: 'my-customer-iso.html'
};
});
UPD
code should be like so:
(function(angular) {
'use strict';
angular.module('docsIsolateScopeDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
$scope.igor = { name: 'Igor', address: '123 Somewhere' };
}])
.directive('myCustomer', function() {
return {
restrict: 'E',
scope: {
customerInfo: '=info'
},
controllerAs: 'vm',
bindToController: true, //the missing line!!
controller: 'dirCtrl',
templateUrl: 'my-customer-iso.html'
};
})
.controller('dirCtrl', ['$scope', dirCtrl]);
function dirCtrl() {
//var vm = this; //no need in this!
}
})(window.angular);
and
Name: {{vm.customerInfo.name}} Address: {{vm.customerInfo.name}}
in the template
I din't manage to fully replicate the initial example from the official angular docs. Directives are quite tricky and I was missing something important about the Isolate Scope "=". I was not getting the value from the controller through the directive with the "=" as the original docs example. #shershen's answer is the right one.
(function() {
'use strict';
var app= angular.module('docsIsolateScopeDirective', []);
app.controller('Controller', [function() {
var vm=this;
vm.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
vm.igor = { name: 'Igor', address: 'Homeless' };
vm.name="John";
}]);
app.directive('myCustomer', function() {
return {
restrict: 'E',
scope: {
},
templateUrl: 'my-customer-iso.html',
controller: 'Controller',
link: function(scope, elem, attr, ctrl) {
var variableName=attr.info;
scope.customerInfo=ctrl[variableName];
}
};
});
})();
Here is the final plunk.
Related
In below code, controllerAs not working with widget and filter directive.
Can you please help me to fix this.
I couldn't figure out where exactly I'm doing wrong. :-(
Expected output:
Widget directive should print the "From widget controller".
Filter directive should print the "From filter directive".
I reproduced the issue here, please check.
HTML:
<div ng-app="myApp">
<body-dir>
<icon-dir>
<filter-dir>
</filter-dir>
</icon-dir>
<widget-dir>
</widget-dir>
</body-dir>
</div>
JS:
var myApp = angular.module('myApp', []);
myApp.controller('filterController', function filterController($scope) {
var vm = this;
vm.test = "From filter directive";
alert("filter");
return vm;
});
myApp.controller('widgetController', ['$scope', function widgetController($scope) {
var oki = this;
oki.widget = "From widget controller";
alert("widget");
return oki;
}])
myApp.directive('bodyDir', function() {
return {
restrict: 'E',
link: function($scope) {
alert('body-dir');
}
};
});
myApp.directive('widgetDir', function() {
return {
restrict: 'E',
controller: 'widgetController',
controllerAs: 'oki',
template: "<span>{{oki.widget}}</span>",
link: function($scope) {
alert('widget-dir');
}
};
});
myApp.directive('filterDir', function() {
return {
controller: 'filterController',
controllerAs: 'vm',
restrict: 'E',
template: "<span>{{vm.test}}</span>",
link: function($scope) {
alert('filter-dir');
}
};
});
myApp.directive('iconDir', function() {
return {
restrict: 'E',
link: function($scope) {
alert('icon-dir');
}
};
});
As far as I remember, controllerAs was introduced in AngularJS v1.2 but in your jsfiddle you are using v1.0.1. That is why controllerAs is not working for widget and filter directives.
I usually use the controllerAs syntax in my Angularjs project, which I think will be more clear in the template.
But in the current web app,I came into an issue as following:
function FirstCtrl() {
this.name = 'First Controller';
}
function fooDirective() {
return {
scope: {
testData:'='
},
name: 'ctrl',
controller: 'FirstCtrl',
controllerAs: 'foo',
template: '<div>{{ foo.name }} {{testData}} {{foo.testData}}</div>',
link: function ($scope, $element, $attrs, $ctrl) {
console.log($ctrl.name);
}
};
}
angular
.module('app', [])
.directive('fooDirective', fooDirective)
.controller('FirstCtrl', FirstCtrl);
the html part is as following:
<div ng-app="app">
<foo-directive test-data="'newdata'"></foo-directive>
</div>
and the live demo is here:
https://jsfiddle.net/baoqger/rbp1wyfa/
And my confusing point is in the template part:
template: '<div>{{ foo.name }} {{testData}} {{foo.testData}}</div>',
The {{testData}} can work well. But the {{foo.testData}} can't. How to fix the issue so I make access the property in the isolated scope object in controllerAs way.
You want to add bindToController: true to your directive definition:
function fooDirective() {
return {
scope: {
testData:'='
},
name: 'ctrl',
controller: 'FirstCtrl',
controllerAs: 'foo',
bindToController: true,
template: '<div>{{ foo.name }} {{testData}} {{foo.testData}}</div>',
link: function ($scope, $element, $attrs, $ctrl) {
console.log($ctrl.name);
}
};
}
Applicable documentation is here (note you have to scroll up a bit bc of their floating menu/header).
This is a followed up question from this.
How can I access a property defined in MyController from MyDirectiveController to change its value or just read it and use it for something else? (commented line in the code).
angular
.module("app",[])
.controller('MyController', MyController)
.controller('MyDirectiveController', MyController)
.directive('myDirective', myDirective);
function MyController() {
var vm = this;
vm.foo = 'fooController';
}
function myDirective() {
return {
restrict: 'E',
scope: true,
controller: MyDirectiveController,
controllerAs: 'vm',
template: '{{vmMy.foo}} - {{vm.foo}}'
}
}
function MyDirectiveController() {
var vm = this;
vm.foo = 'fooDirective';
// vmMyfoo = 'fooDirective';
}
Here is the jsfiddle.
You can use bindToController (available from v1.3.x) setting of directive to bind values to controller instance instead of scope object.
function myDirective() {
return {
restrict: 'E',
scope: {
value: '='
},
controller: MyDirectiveController,
controllerAs: 'vm',
bindToController: true,
template: '{{vm.value}} - {{vm.foo}}'
}
}
and in HTML you pass value to directive like this:
<div ng-controller="MyController as vmMy">
<my-directive value="vmMy.foo"></my-directive>
</div>
I have a directive with dynamic controller which is passed via controller-name property.
Directive:
angular
.module('directives.panel', [])
.directive('panel', panel);
panel.$inject = ['$timeout', '$parse'];
function panel($timeout, $parse) {
var directive = {
restrict: 'EA',
transclude: true,
replace: true,
template: '<div class="panel panel-solid panel-table" ng-transclude></div>',
controller: '#',
name: 'controllerName',
controllerAs: 'panel',
link: linkFunc
};
return directive;
}
}
Is it possible to inject resolve 'taskbook' object into dynamic controller?
When I try to do that I get unknown provider. However injecting Resource service (GroupResource) works fine.
Is it possible to inject resolve in directive controller?
State
angular
.module('taskbooks.taskbook', [
'deployment.group',
'resource.deployment',
'resource.taskbook'
])
.config(TaskbookConfig)
.controller('TaskbookController', TaskbookController);
TaskbookConfig.$inject = ['$stateProvider', '$provide'];
function TaskbookConfig($stateProvider) {
$stateProvider
.state('taskbooks.taskbook', {
url: 'taskbooks/:taskbookId',
parent: 'taskbooks',
views: {
"mainContent#taskbooks": {
controller: 'TaskbookController as taskbook',
templateUrl: 'taskbook/taskbook.tpl.html'
}
},
resolve: {
taskbook: TaskbookPrepare
}
});
}
TaskbookPrepare.$inject = ['$stateParams', 'TaskbookResource'];
function TaskbookPrepare($stateParams, TaskbookResource) {
return TaskbookResource.get({
taskbookId: $stateParams.taskbookId
}).$promise;
}
Directive Controller
angular
.module('deployment.groups', ['resource.group'])
.controller('DeploymentGroupController',DeploymentGroupController);
DeploymentGroupController.$inject = ['$scope', '$element', '$attrs', 'GroupResource', 'taskbook'];
function DeploymentGroupController($scope, $element, $attrs, GroupResource, taskbook) {
}
Sort of... If this directive requires the resolve that is associated with this state, then it's safe to assume it will only ever be used in this state, correct?
Going on that idea, the directive can reference the controller set in the state and you can add the resolve to the controller scope.
Here's a very simplified version of what I'm saying...
angular
.module('taskbooks.taskbook')
.config([ $stateProvider, function ($stateProvider) {
$stateProvider
.state('taskbook', {
url: '/:id',
parent: 'taskbooks',
resolve: {
taskbook: [ '$stateParams', 'TaskbookResource', function ($stateParams, TaskbookResource) {
return TaskbookResource.get({
taskbookId: $stateParams.taskbookId
}).$promise;
}]
},
controller: ['taskbook', function (taskbook) {
this.taskbook = taskbook;
}],
controllerAs: 'taskController'
});
}])
.directive('someDirective', function() {
return {
restrict: 'EAC',
controller: 'taskController',
link: function (scope, el, attr, ctrl) {
var taskbook = ctrl.taskbook;
}
}
});
Do note, I removed a lot of the non-relevant code from yours just to get the point across quicker. Obviously this can be reworked into the structure you've written.
I have two directives and I wanted it to use it like so:
<m-list m-searchable></m-list>
So the two directives were m-list and m-searchable, now I want to access and manipulate the scope of the m-list when I attach the m-searchable directive.
I have this:
'use strict';
angular.module('app')
.directive('mList', function () {
return {
restrict: 'E',
scope: {},
controller: function($rootScope) {
var vm = this;
vm.name = 'joey';
},
controllerAs: 'ctrl',
bindToController: true,
templateUrl: '...'
};
});
And my m-searchable looks like this:
angular.module('app')
.directive('mSearchable', function () {
return {
restrict: 'A',
scope: {},
controllerAs: 'ctrl',
bindToController: true,
replace: true,
controller: function($rootScope, $scope) {
// I want console.log the scope of the directive where I attached the `m-searchable`
}
};
});
I want to console.log the scope of the directive where I attached the m-searchable. How do I access vm.name?
Firstly i think you will encounter $compile error due to both directive try to have their own isolated scope on the same element.
https://docs.angularjs.org/error/$compile/multidir?p0=mList&p1=mSearchable&p2=new%2Fisolated%20scope&p3=%3Cm-list%20m-searchable%3D%22%22%3E
Updated : better approach as suggested at http://juristr.com/blog/2015/01/learning-ng-directives-access-scope-controller/
is to pass the object between controller & directive
<m-list m-searchable name="name"></m-list>
app.directive('mList', function () {
return {
restrict: 'E',
scope: { name: "="},
controller: function($rootScope, $scope) {
var vm = this ;
vm.name = 'joey';
},
controllerAs: 'ctrl',
bindToController: true
};
});
http://plnkr.co/edit/OKVicRzjuH6L0xedF2qB?p=preview