I have two directives: directiveA and directiveB, hanging from the same module in my AngularJS application.
They are called at the same level in the same HTML template, so we could say they are brothers.
<directive-a>
<directive-b>
Both of them have an own method hanging from its scope, like this:
$scope.clickOkey = function () {
... whatever
};
They both have a 'clickOkey' method, but their behaviours are different.
My problem comes out when I try to call the 'clickOkey' of directiveA from directiveA's template. It executes the 'clickOkey' from directiveB.
Inside directiveA's own template:
<label ng-click="clickOkey()">Okey</label>
They are placed at same level so there it shouldn't be way for them to share their $scope or misunderstanding methods.
Also, is important to say that if I change the method's name to 'clickOkeyA', for example, it takes the right method, so the template can access to its scope without problems.
What am I missing?
Thanks for your help!
Edit:
Both directives are isolated and have a controller, and inside each one of them is defined a 'clickOkey' method. There are two methods with the same name.
Both directives are like this:
angular.module('myModule').directive('directiveA', function () {
return {
restrict: 'AE',
templateUrl: '/whatever.html',
controller: function ($scope, $http, $rootScope) {
$scope.clickOkey = function () {
... whatever
};
}
}
});
You should add an isolated scope to your directives :
https://docs.angularjs.org/guide/directive
For your directive it would be :
angular.module('myModule').directive('directiveA', function () {
return {
restrict: 'AE',
templateUrl: '/whatever.html',
scope : {},
controller: function ($scope, $http, $rootScope) {
$scope.clickOkey = function () {
... whatever
};
}
}
});
Related
I am trying to identify why the scope is being shared from the same directive. I have this:
<locator-service
component="redirect"
url="/api/getAllLas1"
redirect="true"></locator-service>
<locator-service
component="signup"
url="/api/getAllLas2"
redirect="false"></locator-service>
Which is basically a directive to handle locations (it works perfectly)
here is the directive for this class
var app = angular.module("frog").directive('locatorService', LocatorService);
function LocatorService() {
return {
restrict: 'E',
templateUrl: '/scripts/frog/location-service.html',
controller: 'locatorController',
controllerAs: 'locatorController',
bindToController: true,
scope: {
component: "#",
url: "#",
redirect: "#"
}
}
}
I have the "scope" declared, I read on a few threads that this would create an isolated scope.
here is the controller class (minimised)
angular.module("frog").controller('locatorController', LocatorController);
function LocatorController($scope, $rootScope, $http, $interval, $document) {
var self = this;
$scope.$watch('url', function () {
if (self.url === undefined) return;
console.log(self.url);
})
}
I attached the debugger to 'console.log(self.url);' and it printed out the following:
/api/getAllLas2
/api/getAllLas2
Could someone help me identify why this is happening?
I have an example on my Github that works perfectly fine for directives not sharing the same scope, but it uses $scope (which I have tried to implement but never even worked slightly)
https://github.com/zackdavidson/tappers-web/tree/master/public/scripts/tappers/app/components/transaction
I have 3 directive with isolate scope and share scope and I want pass a function beteween outermost a innermost directive. The outer and middle has isolate scopes and the middle with inner share the scope. Any suggest ?
Pass the functions of my controller as shown below .
<outer on-edit="helloWorld" ng-model="model" ng-repeat="items in items.objects" ></outer>
In my controller:
$scope.helloWorld = function(){
alert('Hello world');
}
My directive:
angular.module('myApp')
.directive('outer', function () {
return {
restrict: 'E',
replace: true,
scope: {
item: "=ngModel",
onEdit: '&'
},
template: '<div><middle on-edit='onEdit'></middle></div>',
controller : function($scope){
$scope.edit = function(){
$scope.onEdit()();
}
}
};
})
.directive('middle', function () {
return {
restrict: 'E',
replace: true,
scope: {
item : '=ngModel',
onEdit : '&'
},
templateUrl: '<div><inner on-edit='onEdit'></inner></div>'
};
})
.directive('inner', function () {
return {
restrict: 'E',
template: '<div><a ng-click='edit()'>Edit</a></div>'
};
})
And this not work, any ideas?
Thanks
This looks a bad design though, but in the middle directive's template you are using inner directive as follows:
<div><inner on-edit='onEdit'></inner></div>
If you look at it, inner directive has no scope, so the attribute on-edit doesn't make sense there.
If you want to use any method that is present in middle directive can be directly used in inner directive because of shared scope. Think of inner directive as a part of html written in some other html file which will be replaced at run time.
So anything you pass to middle directive is implicitly passed to inner.
So I have this directive that has its own scope but I want to access to a function inside its parent controller. I can do this if the parent controller exposes the function with a $scope.getElementsList(), although I'm trying to avoid the use of $scope and I have the function exposed with self.getElementsList() and the directive cannot reach it.
Directive:
angular.module('myApp').directive('accountBalance', function() {
return {
scope: {
elementId: '=elementid'
},
transclude: true,
restrict: 'E',
templateUrl: '../views_directives/account-balance.html',
controller: function($scope) {
$scope.removeElement = function(){
//this where I want to access the parent function
console.log($scope.$parent.getElementsList());
console.log("ALSO I WANT TO ACCESS THIS DIRECTIVE elementId WITHOUT USING $scope", $scope.elementId);
}
}
};
});
ParentController:
angular.module('myApp').controller('AppDesignCtrl', function ($scope) {
var self = this;
self.elementsList = [];
self.getElementsList = function(){
return self.elementsList;
}
});
I also want to know what is the best way to access, inside the directive controller, the data passed to the directive's $scope.
scope: {
elementId: '=elementid'
},
UPDATE
<div>
<i class="fa fa-arrows element-drag"></i>
<i class="fa fa-trash-o element-remove" ng-click="removeElement()"></i>
</div>
And what about calling functions from the directive template inside the controller of the directive? Do I need to expose them with something like $scope.removeElement()? How do I use this.removeElement() and be able to access it from the template?
Sorry about the long question. I'm trying to set the best practices to my new project since I've been away from angular for a year+.
Thanks in advance
(Going from bottom to top...)
To call functions in the controller without using the scope in Angular >= 1.2, use the controllerAs syntax:
<div ng-controller="AppDesignCtrl as appDesignCtrl">
...
<i class="fa fa-trash-o element-remove" ng-click="appDesignCtrl.removeElement()"></i>
</div>
And removeElement() must be a method of the controller:
angular.module('myApp').controller('AppDesignCtrl', function ($scope) {
...
this.removeElement = function() {
...
};
});
To access the scope data from the controller in Angular >= 1.3, use the new bindToController: true configuration (this is especially useful when combined with the new controllerAs syntax):
angular.module('myApp').directive('accountBalance', function() {
return {
...
scope: {
elementId: '=elementid'
},
controller: function() {
// now elementId is a member of the controller:
console.log(this.elementId);
}
};
});
Having said these, the answer to how you can call getElementsList from the directive would be:
angular.module('myApp').directive('accountBalance', function() {
return {
...
scope: {
elementId: '=elementid',
getElementList: '&'
},
controller: function() {
...
// invoking the expression that was passed to us
var theElements = this.getElementList();
}
};
});
The correct expression should be passed as:
<div ng-controller="AppDesignCtrl as appDesignCtrl">
<account-balance element-id="xxx"
get-elements-list="appDesignCtrl.getElementsList()"></account-balance>
</div>
It is generally not recommended, because directives are meant to be self-contained. It isn't critical if you don't plan to reuse the directive. And wise usage of isolate scope can solve this.
angular.module('myApp').directive('accountBalance', function() {
return {
scope: {
outerScope: '#'
elementId: '='
},
transclude: true,
restrict: 'E',
templateUrl: '../views_directives/account-balance.html',
controller: function($scope) {
console.log("we can use anything from other controller", $scope.outerScope.elementsList)
$scope.elementId = "and share data with any other scope";
}
};
});
Controller is defined as ng-controller="AppDesignCtrl as appDesign", and directive usage is
<account-balance element-id="sharedParentScopeVar" outer-scope="appDesign">
So there won't be any problem if the directive should be moved to other controller.
I guess 'best practice' may be to set up a service that embraces the data and is used by both app controller and directive, so directive controller operates on data items and not DOM elements.
And what about calling functions from the directive template inside
the controller of the directive? Do I need to expose them with
something like $scope.removeElement()?
You surely don't. If there's a need to use functions from outside, you're doing something wrong. Send a message to respective element to run the function if it is DOM-related. Or put the function into the service if it is data-related.
How to call controllers method in directive in below code:
app.controller("main",['$scope','$http',function($scope,$http){
$scope.SelectCollege = function (){
//Code to search college
}
}]);
Directive
angular.module('angucomplete', [] )
.directive('angucomplete', function ($parse, $http, $sce, $timeout) {
link: function($scope, elem, attrs) {
$("#search_value").autocomplete({
source: $scope.localData,
select: function( event, ui ) {
//How to call controller's method here
}
});
}
});
you can include you controller with directive property
The controller option takes a string or a function. When set to a string, the name of the string is
used to look up a controller constructor function registered elsewhere in our application:
angular.module('myApp', [])
.directive('myDirective', function() {
restrict: 'A', // always required
controller: 'SomeController',
link: function(scope, element, attrs, SomeController) {
SomeController.doSomething(scope);
},
})
// elsewhere in our application
// either in the same file or another
// one included by our index.html
angular.module('myApp')
.controller('SomeController', function($scope, $element, $attrs, $transclude) {
// controller logic goes here
})
Assuming the directive is placed inside the controller scope (element with ng-controller )
select: function( event, ui ) {
//How to call controller's method here
//Answer: as shown below
$scope.SelectCollege();
}
You can pass your controller's method to the directive's scope like so:
<autocomplete on-select="SelectCollege()"></autocomplete>
And in your directive define your scope
scope: {
onSelect: "&onSelect"
}
Then within your directive you would just call it as:
$scope.onSelect()
Alternatively you can just use $scope.SelectCollege() inside your directive if your directive is placed within controllers scope and your directive does not declare its own scope but inherits from controllers scope. Example:
<div ng-controller="main">
<autocomplete></autocomplete>
</div>
Can anyone tell me how to include a controller from one directive in another angularJS directive.
for example I have the following code
var app = angular.module('shop', []).
config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/', {
templateUrl: '/js/partials/home.html'
})
.when('/products', {
controller: 'ProductsController',
templateUrl: '/js/partials/products.html'
})
.when('/products/:productId', {
controller: 'ProductController',
templateUrl: '/js/partials/product.html'
});
}]);
app.directive('mainCtrl', function () {
return {
controller: function ($scope) {}
};
});
app.directive('addProduct', function () {
return {
restrict: 'C',
require: '^mainCtrl',
link: function (scope, lElement, attrs, mainCtrl) {
//console.log(cartController);
}
};
});
By all account I should be able to access the controller in the addProduct directive but I am not. Is there a better way of doing this?
I got lucky and answered this in a comment to the question, but I'm posting a full answer for the sake of completeness and so we can mark this question as "Answered".
It depends on what you want to accomplish by sharing a controller; you can either share the same controller (though have different instances), or you can share the same controller instance.
Share a Controller
Two directives can use the same controller by passing the same method to two directives, like so:
app.controller( 'MyCtrl', function ( $scope ) {
// do stuff...
});
app.directive( 'directiveOne', function () {
return {
controller: 'MyCtrl'
};
});
app.directive( 'directiveTwo', function () {
return {
controller: 'MyCtrl'
};
});
Each directive will get its own instance of the controller, but this allows you to share the logic between as many components as you want.
Require a Controller
If you want to share the same instance of a controller, then you use require.
require ensures the presence of another directive and then includes its controller as a parameter to the link function. So if you have two directives on one element, your directive can require the presence of the other directive and gain access to its controller methods. A common use case for this is to require ngModel.
^require, with the addition of the caret, checks elements above directive in addition to the current element to try to find the other directive. This allows you to create complex components where "sub-components" can communicate with the parent component through its controller to great effect. Examples could include tabs, where each pane can communicate with the overall tabs to handle switching; an accordion set could ensure only one is open at a time; etc.
In either event, you have to use the two directives together for this to work. require is a way of communicating between components.
Check out the Guide page of directives for more info: http://docs.angularjs.org/guide/directive
There is a good stackoverflow answer here by Mark Rajcok:
AngularJS directive controllers requiring parent directive controllers?
with a link to this very clear jsFiddle: http://jsfiddle.net/mrajcok/StXFK/
<div ng-controller="MyCtrl">
<div screen>
<div component>
<div widget>
<button ng-click="widgetIt()">Woo Hoo</button>
</div>
</div>
</div>
</div>
JavaScript
var myApp = angular.module('myApp',[])
.directive('screen', function() {
return {
scope: true,
controller: function() {
this.doSomethingScreeny = function() {
alert("screeny!");
}
}
}
})
.directive('component', function() {
return {
scope: true,
require: '^screen',
controller: function($scope) {
this.componentFunction = function() {
$scope.screenCtrl.doSomethingScreeny();
}
},
link: function(scope, element, attrs, screenCtrl) {
scope.screenCtrl = screenCtrl
}
}
})
.directive('widget', function() {
return {
scope: true,
require: "^component",
link: function(scope, element, attrs, componentCtrl) {
scope.widgetIt = function() {
componentCtrl.componentFunction();
};
}
}
})
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
function MyCtrl($scope) {
$scope.name = 'Superhero';
}