AngularJs - Directive scope methods not available in controller - angularjs

I have a directive which inherits its scope from a controller. This Directive has a few methods, when I try to call these methods from the Parent Controller it says method undefined.
Parent Controller:
app.controller('ParentController', ['$scope', function($scope){
$scope.items = []; //Array of Objects.
$scope.someMethod = function(item){
$scope.directiveMethod(item.id);
}
}]);
Directive:
app.directive('someDirective', function(){
return {
restrict: 'A',
link: function(scope, el, attrs){
scope.tableGreen = function(id){
var element = angular.element('[data-cell='+id+']')
element.removeClass('some-class')
element.addClass('new-class')
}
}
}
})
HTML:
<div ng-controller="ParentController">
<div ng-repeat="item in items">
<button ng-click="someMethod(item)" >Green</button>
</div>
<div ng-include="/path/to/some/place">
<div class="some-class" some-directive></div>
</div>
</div>
Is this a right approach? Or is there a better way?

If you are trying to toggle the class of an element triggered from outside of an ng-include one approach could be to utilize ng-class on the element itself:
<div ng-class="{'some-class': item.selected, 'new-class': !item.selected}">content</div>
This could avoid doing the class manipulation via another function and utilize a built-in angular directive, ng-class (https://docs.angularjs.org/api/ng/directive/ngClass).
If you still need to attach other functionality to the element you can of course include ng-class within a directive template.
Take this plnkr for example: http://plnkr.co/edit/Jpr2ayywYYBwiIesmfko?p=preview, it has a set of items in a parent controller, then an ng-include that references those items. The class is changed on elements inside the ng-include from their directive templates with ng-class.

Assign the method to the $parent scope in the directive link.
app.directive('someDirective', function(){
return {
restrict: 'A',
link: function(scope, el, attrs){
scope.$parent.directiveMethod = function(id){
var element = angular.element('[data-cell='+id+']')
element.removeClass('some-class')
element.addClass('new-class')
}
}
}
})

Related

Access parent as well as self controller in Angularjs directive

I have following fiddle where I am using require property of angular directive for accessing parent controller. Now I want to access both parent and self controller in link function of component directive.
<div ng-controller="MyCtrl">
<div screen>
<div component>
</div>
</div>
</div>
Is it possible to access self as well as parent directive controller in postlink function of directive
Have found the solution. I just need to mention same directive name in array of require property of directive definition object. FIDDLE
.directive('component', function() {
return {
scope: true,
require: ['component','^screen'],
controller: function($scope) {
this.componentFunction = function() {
$scope.screenCtrl.doSomethingScreeny();
}
},
link: function(scope, element, attrs, screenCtrl ) {
scope.screenCtrl = screenCtrl
console.log(screenCtrl);
}
}
})

AngularJS: access isolated scope variable within the element tag

Is this possible somehow?
app.directive('myDirective',function(){
return {
restrict: 'A',
scope: {
myName:'=myName'
}
}
}
. . .
<div my-directive my-name="name" ng-class="myName"></div>
or do i need to write my own directive?
Is this possible somehow?
Yes, it is possible (using $parent reference to outer scope, for example) but it doesn't make sense to do it like this. You don't want directive to mess with things outside its sandbox (isolated scope). After all, this is the point of having isolated scope in the first place.
In your case, you just need to use ngClass with the original name, which is already available in the scope (outer):
<div my-directive my-name="name" ng-class="name"></div>
HTML:
<div ng-controller="MyCtrl">
<my-directive my-name="name" />
</div>
JS:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.name = 'World';
}
myApp.directive('myDirective',function(){
return {
restrict: 'E',
scope: {
myName:'=myName'
},
replace: false,
template: 'Hello, {{myName}}!',
controller: function(){}
}
});
Fiddle: http://jsfiddle.net/HB7LU/20538/
Change the template with your HTML code.
If you want to manipulate a class from your controller use:
HTML
<div ng-class="{{myName}}" >
JS
$scope.myName = 'value';

Angular - understanding directive isolated scope method

take a look at the following code:
html:
<body ng-app="myApp">
<div ng-controller="MainController">
<input type="button" ng-click="talk()" value="outside directive" />
<div my-element>
<input type="button" ng-click="talk()" value="inside directive" />
</div>
</div>
</body>
js:
var app = angular.module('myApp', []);
app.controller('MainController', function($scope){
$scope.talk = function() {
alert('HELLO1');
}
});
app.directive('myElement', function(){
return {
restrict: 'EA',
scope: {},
controller: function($scope) {
$scope.talk = function() {
alert('HELLO2');
}
}
};
});
as you can see, there's a controller, which nests a directive.
there are 2 buttons, one in controller level (outside of directive), and one is inside the directive my-element.
the main controller defines a scope method talk, the nested directive controller also defines a method - talk - but keep in mind that directive has isolated scope (i'd expect that talk method won't be inherited into directive's scope).
both buttons result an alert of 'HELLO 1', while i expected the second button (inside directive) to alert 'HELLO 2' as defined in directive controller, but it doesn't - WHY?
what am i missing here? how could i get a result when the second button will alert 'HELLO 2' but with the same method name ("talk") ?
thanks all
If you want the inner content to use the directive scope, you need to use manual transclusion:
app.directive('myElement', function(){
return {
restrict: 'EA',
scope: {},
transclude: true,
link: function(scope, element, attr, ctrl, transclude) {
transclude(scope, function(clone, scope) {
element.append(clone);
});
},
controller: function($scope) {
$scope.talk = function() {
alert('HELLO2');
}
}
};
});
By default, transcluded content uses a sibling of the directive scope. I actually don't know how angular handles DOM content for directives that don't use transclude (which is what makes this an interesting question), but I would assume from the behavior you are seeing that those elements use the directive's parent scope by default.
This will work for you
<body ng-app="myApp">
<div ng-controller="MainController">
<input type="button" ng-click="talk()" value="outside directive" />
<div my-element></div>
</div>
</body>
app.directive('myElement', function(){
return {
restrict: 'A',
template: '<input type="button" ng-click="talk()" value="inside directive">',
replace: true,
scope: {},
controller: function($scope) {
$scope.talk = function() {
alert('HELLO2');
}
}
};
});

how to call a controller function from ng-click directive?

I have two directives and a controller, the problem is that i can't call a function 'addMarkers()' of the controller from my directive .
i have the following codes:
derectives.js
app
.directive('collection', function () {
var tpl = '<ul><member ng-repeat="member in collection" member="member"></member></ul>';
return {
restrict: "E",
replace: true,
scope: {
collection: '='
},
template: tpl
}
})
app
.directive('member', function ($compile) {
var tpl = '<li><a ng-click="addMarkers(member)" >{{member.title}}</a>'+
'<input class="align" ng-if="!member.children" type="checkbox" ng-checked="true"/></li>';
return {
restrict: "E",
replace: true,
scope: {
member: '='
},
template: tpl,
link: function (scope, element, attrs) {
if (angular.isArray(scope.member.children)) {
element.append("<collection collection='member.children'></collection>");
$compile(element.contents())(scope)
}
}
}
})
controller.js
app
.controller('IndexCtrl', function($scope, itemProvider){
itemProvider.getItems().success(function(data){
$scope.items = data;
});
$scope.addMarkers = function(item){
alert("Helloo");
$scope.markers = itemProvider.addMarkers();
}
});
index.html
<div id="menu" ng-controller="IndexCtrl">
<nav>
<h2><i class="fa fa-reorder"></i>All Categories</h2>
<collection collection='items'></collection>
</nav>
</div>
$rootScope is the global scope which should be used only when required. It should be kept as clean as possible to avoid pollution of scope variables.
In order to access parent method from isolated scope you can use $parent service, as shown below:
scope.$parent.addMarkers();
example: basic example
In your case, The directive that wants to access the parent is again called from inside another directive,hence for such cases you can use $parent.$parent,
scope.$parent.$parent.addMarkers(); as shown in the following:
example:example for your case
This can be done if the number of directives using the parent scope is limited. If the hierarchy is long, then using multiple $parent makes the code clumsy. In those cases, it is preferable to add the parent method inside a service and call the service itself from the directives.
Example: service example
I should use $rootScope instead of $scope like below,
$rootScope.addMarkers = function(item){
alert("Helloo");
$scope.markers = itemProvider.addMarkers();
}

Set attribute value of angular directive from another controller

Angular directive;
.directive('ngFilemanager', function () {
return {
restrict: 'EA',
scope: {
thefilter: '=',
},
link: function (scope, element, attrs) {
},
templateUrl: '/templates/filemanager.html',
controller: FileManagerController
}
Html:
<div id="testcontainer" ng-controller="OtherController">
...
<div ng-click="vm.myfunction">Set Filter</div>
...
<div id="thefilemanager" ng-filemanager thefilter=""></div>
...
</div>
How can i set thefilter value in a function of OtherController?
I tried setting the attribute value by jquery but my ng-view isn't updated correctly then.
You've got bi-directional isolated scope so:
function OtherController($scope){
$scope.myfilter= "";
$scope.setFilter = function(what){
$scope.myfilter = what;
}
}
and HTML:
<div id="testcontainer" ng-controller="OtherController">
<div ng-click="setFilter('fun')">Set Filter</div>
<div id="thefilemanager" ng-filemanager thefilter="myfilter"></div>
</div>
Then when you change $scope.myfilter in the OtherController's scope, scope.thefilter changes in your directive's scope.
If the "other" controller is not a direct parent, you could use $emit or $broadcast depending on where the target is.
Here's an example using $broadcast instead:
app.controller('MainCtrl', function($scope) {
$scope.setFilter = function(what){
$scope.$broadcast('setFilter', what);
}
});
then inside your directive you can listen:
link: function (scope, element, attrs) {
scope.$on('setFilter', function(e, what){
scope.thefilter = what;
});
},
To make it work anywhere, you can $broadcast from $rootScope, but at that point you might want to re-evaluate why you have to do this. Angular itself does this a lot, for example, routeChangeSuccess event, but that doesn't mean you should do it.
This will work if the other controller is a parent of ngFilemanager
<div id="thefilemanager" ng-filemanager thefilter="theFilterValue"></div>
In some other controller
...
$scope.theFilterValue = 'some other value';
...
Look the doc's isolate scope on directives section

Resources