Background
I have a top level directive which needs to be accessed by a controller. Please consider this Plunk.
Directive
app.directive('topDirective', ['$compile', function($scope){
return {
scope: {},
restrict: 'E',
template: '<h3>Top Directive</h3><p><button ng-click="CallMe()">Click Me</button></p>',
controller: function($scope) {
var self = {};
$scope.CallMe = function(){
alert('Call Me');
};
},
link: function($scope, $elem, $attrs, $ctrl) {
}
};
}]);
Controller that needs access
app.controller('subController', [
'$scope',
function($scope){
var self = {};
$scope.CallDirective = function() {
alert('>>> Replace by call to directive function CallMe (somehow) <<<')
};
}]);
Question
What do I need to do to replace this line:
alert('>>> Replace by call to directive function CallMe (somehow) <<<')
by an actual call to the CallMe() function in the directive?
If not possible directly, is there a way to share functionally that both the directive and controller can use? My first thought would be a service, but it would need to do DOM manipulation in the real scenario, so that's not an option.
Any suggestions?
in Controller emit the event
app.controller('subController', [
'$scope','$rootScope',
function($scope,$rootScope){
var self = {};
$scope.CallDirective = function() {
var data ='This is new data';
$rootScope.$emit('callDirective',data);
};
}]);
and in directive you can do it like
app.directive('topDirective', ['$compile', function($scope){
return {
scope: {},
restrict: 'E',
template: '<h3>Top Directive</h3><p><button ng-click="CallMe()">Click Me</button></p>',
controller: function($scope,$rootScope) {
var self = {};
$scope.CallMe = function(data){
alert(data);
};
$rootScope.$on('callDirective',function(type,data){
$scope.CallMe(data);
});
},
link: function($scope, $elem, $attrs, $ctrl) {
}
};
}]);
Related
Let us say I have this html:
<div ng-controller="MyCtrl">
<br>
<my-directive my-name="name">Hello, {{name}}!</my-directive>
</div>
with this simple controller:
myApp.controller('MyCtrl', function ($scope) {
$scope.name = 'Superhero';
});
And I have a directive in which I want to change the 'name' using require like this:
myApp.directive('myDirective', function($timeout) {
var controller = ['$scope', function ($scope) {
$scope.name = "Steve";
}];
return {
restrict: 'EA',
require: 'myName',
controller: controller,
link: function(scope, element, attrs, TheCtrl) {
TheCtrl.$render = function() {
$timeout(function() {
TheCtrl.$setViewValue('StackOverflow');
}, 2000);
};
}
};
});
But throws:
Error: No controller: myName
Here is the fiddle
But if I implement it using ng-model, works. Look here in this other fiddle
I have read that if you use 'require' in a directive, you need to have a controller for it.
So:
What I'm doing is wrong? It is not in this way? I need to do any other thing?
Well finally I got it.
Essencially what I'm trying to do is something called: 'Communication between directives using controllers'. I have found an article explaining this, and helped me a lot:
The view:
<div ng-controller="MyCtrl">
<br>
<my-directive my-name>Hello, {{name}}!</my-directive>
</div>
As you see above, there are two directives: my-directive and my-name. I will call inside my-directive a function from the controller of my-name directive using require.
myDirective:
myApp.directive('myDirective', function($timeout) {
return {
require: 'myName',
link: function(scope, element, attrs, myNameCtrl) {
$timeout(function() {
myNameCtrl.setName("Steve");
}, 9000);
} // End of link
}; // return
});
myName:
myApp.directive('myName', function($timeout) {
var controller = ['$scope', function ($scope) {
// As I tried, this function can be only accessed from 'link' inside this directive
$scope.setName = function(name) {
$scope.name = name;
console.log("Inside $scope.setName defined in the directive myName");
};
// As I tried, this function can accessed from inside/outside of this directive
this.setName = function(name) {
$scope.name = name;
console.log("Inside this.setName defined in the directive myName");
};
}];
return {
controller: controller,
link: function(scope, element, attrs, localCtrl) {
$timeout(function() {
localCtrl.setName("Charles");
}, 3000);
$timeout(function() {
scope.setName("David");
}, 6000);
} // End of link function
};
});
Interesting and works like a charm. Here is the fiddle if you want to try it out.
Also, you can get communication between directives using events. Read this answer here on SO.
myPage.html
<div ng-controller="MyPageCtrl">
<my-custom-directive arg1="{{currentObj.name}}"></my-custom-directive>
<div>
in myPageCtrl.js (Controller)
app.controller("MyPageCtrl", ["$scope", function ($scope) {
$scope.currentObj = {"name":"Collin"};
}]);
And this is how my directive code looks like
app.directive("myCustomDirective", [function () {
return {
restrict: "E",
controller: "MyCustomDirCtrl"
};
}]);
Finally here's my directive's controller,
app.controller("MyCustomDirCtrl", ["$attrs", function ($attrs) {
var arg = $attrs.arg1;
alert('Arg '+arg);
}]);
The alert just displays "{{currentObj.name}}" and not the value of the name property of currentObj.
Please can you suggest me ways to figure this out.
Thanks.
Not sure why did you use $attrs for a controller. Just use a normal $scope.
myPage.html
<div ng-controller="MyPageCtrl">
<my-custom-directive arg1="{{currentObj.name}}"></my-custom-directive>
<div>
myPageCtrl.js (Controller)
app.controller("MyPageCtrl", ["$scope", function ($scope) {
$scope.currentObj = {"name":"Collin"};
}]);
myCustomDirective
app.directive("myCustomDirective", [function () {
return {
restrict: "E",
controller: "MyCustomDirCtrl"
};
}]);
Directive's controller (change $attrs to $scope here),
app.controller("MyCustomDirCtrl", ["$scope", function ($scope) {
var arg = $scope.arg1;
alert('Arg '+arg);
}]);
Instead of accessing the attr from your controller, you could access it from your directive using the link function.
app.directive("myCustomDirective", [function () {
return {
restrict: "E",
controller: "MyCustomDirCtrl",
link: function(scope, element, attr) {
alert(attr.arg1);
}
};
}]);
I have a directive as the following:
app.directive('fileInput', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.bind('change', function () {
$parse(attrs.fileInput)
.assign(scope, element[0].files)
scope.$apply();
});
scope.$watch('files', function () {
console.log(scope.files);
});
},
controller: function ($scope, $element, $attrs) {
$element.bind('change', function () {
$parse($attrs.fileInput)
.assign($scope, $element[0].files)
$scope.$apply();
});
$scope.$watch('files', function () {
console.log($scope.files);
});
}
}
EDIT
and this is controller:
controllers.controller('RegisterCtrl', ['$scope', '$routeParams', '$location', '$http', 'Restangular', 'ServiceRepository',
function($scope, $routeParams, $location, $http, Restangular, ServiceRepository)
{
$scope.regService = function () {
$scope.error = {};
$scope.submitted = true;
var fd = new FormData();
fd.append("model", angular.toJson($scope.services));
console.log($scope.files);
}
}
And this is view file
<input type="file" id="boarding-picture_where_sleeping" class="form-control" file-input="files" multiple>
Additional info, regService() method is called when submitting the form
and when I debug $scope.files, it's available in console tab. but in my controller, it's undefined
so how to sync it between the directive and controller
Thanks
it's working now. the problem was caused I used 2 nested directives :)
Thanks guys
I would use scope and bindToController attributes in the directive definition, like in the following snippet from this blog:
app.directive('someDirective', function () {
return {
scope: {
name: '='
},
controller: function () {
this.name = 'Pascal';
},
controllerAs: 'ctrl',
bindToController: true,
template: '<div>{{ctrl.name}}</div>'
};
});
It requires use of the controllerAs syntax too, but you should be using that anyway as it is much more clear than passing $scope around everywhere and dealing with prototypical inheritance. This is recommended in John Papa's AngularJS Style Guide
I have created a directive.
angular.module('app')
.directive('navtree', function (service) {
return {
restrict: 'A',
scope: {},
link: function (scope, el) {
scope.loadNavtree = function(){
service.data()
.then(function (data) {
///Do something
});
}
scope.loadNavtree();
}
};
});
from my controller I can access the method using
$scope.$parent.$$childHead.loadNavtree();
Though this is working, I feel that this is not the right approach. I want to understand what are the disadvantages of accessing function defined in directive from your controller like this.
I looked this link but I was not able to follow
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
/// How to call takeTablet() available in directive from here?
});
app.directive('focusin', function factory() {
return {
restrict: 'E',
replace: true,
template: '<div>A:{{internalControl}}</div>',
scope: {
control: '='
},
link : function (scope, element, attrs) {
scope.takeTablet = function() {
alert('from directive');//
}
}
};
});
this is not the correct approach because angular do not recommend to use its private variable to access to directive function so you need to get a good approach to do that here is an example to access the directive function from controller.
If you want to use isolated scopes you can pass a control object using bi-directional binding ('=') of a variable from the controller scope. In this way you can control also several instances of the same directive on a page.
plunkr
Controller/Directive:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.focusinControl = {
};
});
app.directive('focusin', function factory() {
return {
restrict: 'E',
replace: true,
template: '<div>A:{{internalControl}}</div>',
scope: {
control: '='
},
link : function (scope, element, attrs) {
scope.internalControl = scope.control || {};
scope.internalControl.takenTablets = 0;
scope.internalControl.takeTablet = function() {
scope.internalControl.takenTablets += 1;
}
}
};
});
HTML:
<button ng-click="focusinControl.takeTablet()">Call directive function</button>
<h4>In controller scope:</h4>
{{focusinControl}}
<h4>In directive scope:</h4>
<focusin control="focusinControl"></focusin>
<h4>Without control object:</h4>
<focusin></focusin>
I need to call a function which belongs to the $scope of a ng-directive used in my Angular application.
Let's say the directive is defined like this:
.directive('my-directive', ['$document', '$timeout', function ($document, $timeout) {
return {
restrict: 'E',
replace: true,
scope: {
// ....
},
controller: ['$scope', function ($scope) {
$scope.myFunction= function (mouseEnter) {
// ...
};
}
};
}]);
I need to call myFunction from my controller (let's call it my-controller) which is the controller of the view where my directive is placed.
Is it possible to do it? (eventually modifying the directive)
EDIT : The already answered question provided (proposed for edit) is similar to mine by it's not clear to me or it doesn't apparently solve the specific problem I proposed.
EDIT 2: starting from Dan M. answer (without taking mouseenter/mouseleave in consideration. just trying to make the two controllers communicate with each other), I broadcasted my event to my directive's controller through $rootScope (as there is there is no parent-child relation between the two controllers) by:
console.log("let's broadcast the event.."); // this is printed
$rootScope.$broadcast('callDirectiveControllersFunction'); // I even tried with $scope in place of $rootScope and $emit in place of $broadcast
and by receving it (within the directive's controller) by:
var myFunction = function(){
// ...
}
$scope.$on('callDirectiveControllersFunction', function (){
console.log("event received"); // this is not printed
callMyFunction();
});
// I even tried using $rootScope in place of $scope
However in no case (see comments in code) the event is received
You can call a controller function inside the link block. You can also $emit an event in the directive and listen to the it in the controller (maybe there is a use case for that).
It seems that you want to call it on mouseenter. You can do that by binding to the mouseenter event in the directive link. The catch is you need to $apply the changes.
Take a look at the following piece of code, which contains all 3 examples: http://jsbin.com/cuvugu/8/. (also pasted below)
Tip: You might want to pay attention to how you name your directives. To use a directive as my-directive you need to name it as myDirective.
var app = angular.module('App', []);
app.directive('myDirective', function () {
function directiveLink(scope){
scope.$emit('customEvent');
}
return {
restrict: 'EA',
scope: {},
link: directiveLink,
controller: function ($scope) {
$scope.bar = 'bar';
$scope.myFunction = function () {
$scope.bar = 'foobar1';
};
$scope.$on('customEvent', function (){
$scope.myFunction();
});
},
template: "Foo {{bar}}"
};
});
app.directive('anotherDirective', function () {
function directiveLink(scope){
scope.myFunction();
}
return {
restrict: 'EA',
scope: {},
link: directiveLink,
controller: function ($scope) {
$scope.bar = 'bar';
$scope.myFunction = function () {
$scope.bar = 'foobar2';
};
},
template: "Foo {{bar}}"
};
});
app.directive('mouseDirective', function () {
function directiveLink(scope, element){
element.bind('mouseenter', function(){
scope.$apply(function(){
scope.myFunction();
});
});
element.bind('mouseleave', function(){
scope.$apply(function(){
scope.myOtherFunction();
});
});
}
return {
restrict: 'EA',
link: directiveLink,
controller: function ($scope) {
$scope.bar = 'no';
$scope.myFunction = function () {
$scope.bar = 'yes';
};
$scope.myOtherFunction = function () {
$scope.bar = 'no';
};
},
template: "Mouse Enter: {{bar}}"
};
});
I also included an example with a distinct controller in the JS Bin link. That doesn't really change anything, but it seems to be an important part of your question. Here's the code block:
var app = angular.module('App', []);
app.controller('myController', function($scope){
$scope.bar = 'foo';
$scope.myFunction = function(){
$scope.bar = 'foobar3';
};
});
app.directive('lastDirective', function () {
function directiveLink(scope){
scope.myFunction();
}
return {
restrict: 'EA',
scope: {},
link: directiveLink,
controller: 'myController',
template: "Foo {{bar}}"
};
});