I have the following code. Why doesn't the vm.name work? Why is the this inside the controller not being detected? Haven't I defined a closed scope for the directive?
What am I doing wrong?
var mod = angular.module('myApp', []);
mod.directive('myObj', myObject);
function myObject(){
return {
restrict: 'E',
templateUrl: 'my-obj.html',
scope: {},
controller: myController
};
function myController(){
var vm = this;
vm.name="vfdfbdn";
}
}
To use this in controller inside directive you need to use controllerAs: 'ctrl' but then in template you will need to prefix all name with {{ctrl.name}} or you can use $scope like:
function myController($scope) {
$scope.name="vfdfbdn";
}
function myObject(){
return {
restrict: 'E',
template: '<div>{{c.name}}</div>',
scope: {},
controller: myController,
controllerAs: 'c'
};
function myController(){
var vm = this;
vm.name="vfdfbdn";
}
};
Please see this question to understand the things
You need to tell Angular how you are going to reference the this from the controller in the view.
function myObject(){
return {
restrict: 'E',
templateUrl: 'my-obj.html',
scope: {},
controller: myController,
controllerAs: 'ctrl'
};
}
Now you can reference everything that you assigned to this, that you named vm in your controller with ctrl.
I used ctrl to show that there is no correlation between the name you use to refere to it in the view, setted with controllerAs and the name you give to this inside the controller function. It is a normal to reference different controllers with different controllerAs references so you can now which controller they refer to in the view.
Try this:
function myObject() {
return {
restrict: 'E',
templateUrl: 'my-obj.html',
scope: {},
bindToController: true,
controller: myController
};
}
myController.$inject = ['$scope'];
function myController($scope){
var vm = this;
vm.name="vfdfbdn";}
Related
ShowPopover doesn't work when you click on the directive. Could you please help me identify the cause of the problem?
Directive:
angular.module('landingBuilder').directive('popoverDirective', popoverDirective);
function popoverDirective() {
return {
restrict: 'E',
templateUrl: '/libs/landing-builder/directive/popover-directive/popover-directive.html',
controller: controller,
controllerAs: 'vm',
transclude: true,
bindToController: true
};
function controller($element, $document){
var vm = this;
vm.showPopover = showPopover();
function showPopover() {
console.log('show popover');
};
}
}
Template:
<div class="input-layover popup-target" ng-click="vm.showPopover"</div>
Use showPopover() instead of showPopover here.
myApp.directive('myDirective', function() {
return {
restrict: 'E',
template: '<button ng-click=vm.showPopover()>hello</button>',
controller: controller,
controllerAs: 'vm',
transclude: true,
bindToController: true
};
function controller($element, $document) {
var vm = this;
vm.showPopover = showPopover;
function showPopover() {
console.log('show popover');
};
}
});
Working Fiddle
Look at this fiddle: https://jsfiddle.net/ns0pe1ur/
You made some mistakes in your code:
it is not proper html
you need to call function on ng-click, not inside you controller
Your template should look like this:
<div class="input-layover popup-target" ng-click="vm.showPopover()"></div>
And you controller like this:
function controller($element, $document) {
var vm = this;
vm.showPopover = showPopover;
function showPopover() {
console.log('show popover');
};
}
I have an Angular 1.3 module that looks something like this (directive that requires the presence of a parent directive, using controllerAs):
angular.module('fooModule', [])
.controller('FooController', function ($scope) {
this.doSomething = function () {
// Accessing parentDirectiveCtrl via $scope
$scope.parentDirectiveCtrl();
};
})
.directive('fooDirective', function () {
return {
// Passing in parentDirectiveCtrl into $scope here
link: function link(scope, element, attrs, parentDirectiveCtrl) {
scope.parentDirectiveCtrl = parentDirectiveCtrl;
},
controller: 'FooController',
controllerAs: 'controller',
bindToController: true,
require: '^parentDirective'
};
});
Here I'm just using $scope to pass through parentDirectiveCtrl, which seems a little clunky.
Is there another way to access the require-ed controller from the directive's controller without the linking function?
You must use the link function to acquire the require-ed controllers, but you don't need to use the scope to pass the reference of the controller to your own. Instead, pass it directly to your own controller:
.directive('fooDirective', function () {
return {
require: ["fooDirective", "^parentDirective"],
link: function link(scope, element, attrs, ctrls) {
var me = ctrls[0],
parent = ctrls[1];
me.parent = parent;
},
controller: function(){...},
};
});
Be careful, though, since the controller runs prior to link, so within the controller this.parent is undefined, until after the link function runs. If you need to know exactly when that happens, you can always use a controller function to pass the parentDirective controller to:
link: function link(scope, element, attrs, ctrls) {
//...
me.registerParent(parent);
},
controller: function(){
this.registerParent = function(parent){
//...
}
}
There is a way to avoid using $scope to access parent controller, but you have to use link function.
Angular's documentation says:
Require
Require another directive and inject its controller as the fourth
argument to the linking function...
Option 1
Since controllerAs creates namespace in scope of your controller, you can access this namespace inside your link function and put required controller directly on controller of childDirective instead of using $scope. Then the code will look like this.
angular.module('app', []).
controller('parentController', function() {
this.doSomething = function() {
alert('parent');
};
}).
controller('childController', function() {
this.click = function() {
this.parentDirectiveCtrl.doSomething();
}
}).
directive('parentDirective', function() {
return {
controller: 'parentController'
}
}).
directive('childDirective', function() {
return {
template: '<button ng-click="controller.click()">Click me</button>',
link: function link(scope, element, attrs, parentDirectiveCtrl) {
scope.controller.parentDirectiveCtrl = parentDirectiveCtrl;
},
controller: 'childController',
controllerAs: 'controller',
bindToController: true,
require: '^parentDirective'
}
});
Plunker:
http://plnkr.co/edit/YwakJATaeuvUV2RBDTGr?p=preview
Option 2
I usually don't use controllers in my directives at all and share functionality via services. If you don't need to mess with isolated scopes of parent and child directives, simply inject the same service to both of them and put all functionality to service.
angular.module('app', []).
service('srv', function() {
this.value = '';
this.doSomething = function(source) {
this.value = source;
}
}).
directive('parentDirective', ['srv', function(srv) {
return {
template: '<div>' +
'<span ng-click="srv.doSomething(\'parent\')">Parent {{srv.value}}</span>' +
'<span ng-transclude></span>' +
'</div>',
transclude: true,
link: function(scope) { scope.srv = srv; }
};
}]).
directive('childDirective', ['srv', function(srv) {
return {
template: '<button ng-click="srv.doSomething(\'child\')">Click me</button>',
link: function link(scope) { scope.srv = srv; }
}
}]);
Plunker
http://plnkr.co/edit/R4zrXz2DBzyOuhugRU5U?p=preview
Good question! Angular lets you pass "parent" controller. You already have it as a parameter on your link function. It is the fourth parameter. I named it ctrl for simplicity. You do not need the scope.parentDirectiveCtrl=parentDirectiveCtrl line that you have.
.directive('fooDirective', function () {
return {
// Passing in parentDirectiveCtrl into $scope here
link: function link(scope, element, attrs, ctrl) {
// What you had here is not required.
},
controller: 'FooController',
controllerAs: 'controller',
bindToController: true,
require: '^parentDirective'};});
Now on your parent controller you have
this.doSomething=function().
You can access this doSomething as
ctrl.doSomething().
I have this directive definition and want to pass currentScriptPath to the TestController.
How do I do that?
(function(currentScriptPath){
angular.module('app', [])
.directive('test', function () {
return {
restrict: 'E',
scope: {},
templateUrl: currentScriptPath.replace('.js', '.html'),
replace: true,
controller: TestController,
controllerAs: 'vm',
bindToController: true
};
});
})(
(function () {
var scripts = document.getElementsByTagName("script");
var currentScriptPath = scripts[scripts.length - 1].src;
return currentScriptPath;
})()
);
TestController.$inject = ['$scope'];
function TestController($scope) {
// try to access $scope.currentScriptPath here
}
As you want to access currentScriptPath in your directive controller. You need to only attach that variable into your current scope inside link function of directive & that scope would be make currentScriptPath available to you controller TestController scope because you have used bindToController: true, in your directive.
Markup
<div ng-controller="Ctrl">
<test></test>
</div>
Directive
(function(currentScriptPath) {
angular.module('app', [])
.directive('test', function() {
return {
restrict: 'E',
scope: {},
templateUrl: currentScriptPath.replace('.js', '.html'),
replace: true,
controller: TestController,
controllerAs: 'vm',
bindToController: true,
link: function(scope, element, attrs) {
scope.currentScriptPath = currentScriptPath; //will update the value of parent controller.
}
};
});
})(
(function() {
var scripts = document.getElementsByTagName("script");
var currentScriptPath = scripts[scripts.length - 1].src;
return currentScriptPath;
})()
);
Controller
function TestController($scope, $timeout) {
var vm = this;
$timeout(function() {
alert($scope.currentScriptPath) //gets called after link function is initialized
})
}
Demo Plunkr
How we can get particular isolated scope of the directive while calling link function from controller(parent)?
I am having a directive and repeating it using ng-repeat. Whenever a button in the directive template is clicked it will call a function- Stop() in directive controller which in-turn calls function test() in parent controller, inside test() it will call a method dirSample () in directive's link function.
When I print the scope inside dirSample(), it prints the scope of the last created directive not the one which called it.
How can I get the scope of the directive which called it?
Find the pluker here
.directive('stopwatch', function() {
return {
restrict: 'AE',
scope: {
meri : '&',
control: '='
},
templateUrl: 'text.html',
link: function(scope, element, attrs, ctrl) {
scope.internalControl = scope.control || {};
scope.internalControl.dirSample = function(){
console.log(scope)
console.log(element)
console.log(attrs)
console.log(ctrl)
}
},
controllerAs: 'swctrl',
controller: function($scope, $interval)
{
var self = this;
self.stop = function()
{
console.log($scope)
$scope.meri(1)
};
}
}});
full code in plunker
I've changed the binding of your function from & to = since you need to pass a parameter. This means some syntax changes are in order, and also you need to pass the scope along the chain if you want to have it all the way at the end:
HTML:
<div stopwatch control="dashControl" meri="test"></div>
Controller:
$scope.test = function(scope)
{
console.log(scope);
$scope.dashControl.dirSample(scope);
}
Directive:
.directive('stopwatch', function() {
return {
restrict: 'AE',
scope: {
meri : '=',
control: '='
},
templateUrl: 'text.html',
link: function(scope, element, attrs, ctrl) {
scope.internalControl = scope.control || {};
scope.internalControl.dirSample = function(_scope){
console.log(_scope);
}
},
controllerAs: 'swctrl',
controller: function($scope, $interval)
{
var self = this;
self.stop = function()
{
console.log($scope);
$scope.meri($scope);
};
}
}});
Plunker
I have an element directive and I want to know if I can get parameters from routeProvider to render my template and set it up in my controller.
adminDash.directive('hospitals', function() {
return {
restrict: 'E',
templateUrl: 'www/partials/admin/hospitals.html',
controller: 'AdminHospitalsController',
controllerAs: 'hospitalsCtrl',
};
});
How can I get any parameters in my element directive?
You can isolate the scope of the directive, like this:
adminDash.directive('hospitals', function() {
return {
restrict: 'E',
templateUrl: 'www/partials/admin/hospitals.html',
controller: 'AdminHospitalsController',
controllerAs: 'hospitalsCtrl',
scope: {
paramValue: '&',
paramVariable: '=',
},
};
});
check this to understand better https://thinkster.io/egghead/isolate-scope-am/
There are 2 ways to do it: both involve using $routeParams service that allows you to retrieve current set of route parameters.
Given that you have an url: http://example.com/#/hospitals/foobar and a route /hospitals/:hospital/ configured in your $routeProvider, you can:
1.Inject $routeParams into your directive:
adminDash.directive('hospitals', function($routeParams) {
return {
restrict: 'E',
templateUrl: 'www/partials/admin/hospitals.html',
controller: 'AdminHospitalsController',
controllerAs: 'hospitalsCtrl',
link: function(scope, element){
scope.hospital = $routeParams.hospital;
}
};
});
2.Inject $routeParams into AdminHospitalsController
adminDash.controller('AdminHospitalsController', function($scope, $routeParams) {
$scope.hospital = $routeParams.hospital;
}
Both method will result in having hospital=foobar in your directive's scope;