I've written my own directive from which I want to call a method on the parent ng-controller. I've tried doing that by adding require:'ngController' in my directive. However the returned controller is just an empty object. I'm running 1.4.8.
<div ng-controller="myController">
<div my-directive></div>
</div>
app.directive('myDirective', function() {
return {
restrict: 'A',
scope: false,
require: '^ngController',
link: function (scope, $element, attrs, controller) {
//controller is empty object..
};
}
});
Update: My misstake was that I added a method to the controllers scope when I should add it directly to the controller instead.
app.controller('myController', function($scope) {
$scope.myMethod = function() {}; //Not callable from controller passed to directive
this.myMethod = function() {}; //IS callable from controller passed to directive
});
You can call the parent controller via scope if your directive doesn't have an isolated scope, and you don't need to require ngController.
angular.module('app', [])
.controller('controller', function($scope) {
$scope.greet = function() {
return 'hi'
};
})
.directive('testDirective', function() {
return {
restrict: 'E',
template: '<h1>{{ greet() }}</h1>'
};
});
Output:
hi
Plnkr: http://plnkr.co/edit/uGXC5i1GjphcZCPDytDG?p=preview
The function needs to be available in the directive's scope. So if your directive is in the scope of the controller it is:
scope.fn();
Related
I've created a directive which I called my-tree, and I'm calling this directive from a view exemple-tree-view.html as following:
<my-tree ng-model="sampleTreeView.listNoeuds" ... />
this view's controller called sampleTreeView.
In my directive's link function I have a function that returns some data, which I affect to scope variable declared in the directive's controller, as following :
function linkFn(scope, element, attrs) {
//some code
scope.createNode = function ($event) {
var sel = $(element).jstree(true).create_node($($event.currentTarget)[0].closest('.jstree-node').id);
if (sel) {
$(element).jstree(true).edit(sel, '', function (node, success, cancelled) {
scope.treeActionsResult.createdNode = node;
});
}
};
//some code
}
My question is how can I get the scope.treeActionsResult.createdNode value in the sampleTreeView controller, since it's the controller for the exemple-tree-view.html where I call my directive.
You can use shared scope between the directive and controller by removing the scope property
like in this example:
MyApp.directive('studentDirective', function () {
return {
template: "{{student.name}} is {{student.age}} years old !!",
replace: true,
restrict: 'E',
controller: function ($scope) {
console.log($scope);
}
}
});
Still you have the $scope object, but in this case the scope object is shared with parent controller's scope.
You can read more about it fron the following link
Understanding Scope in AngularJs Custom Directive
If you don't create isolated scope for your directive then you can access directive scope values from your controller. like bellow
your controller and directive:
app.controller('MainCtrl', function($scope) {
$scope.value = 1;
});
app.directive('myTree', function() {
return {
restrict: 'AE',
link: function(scope, element, attrs) {
scope.values = {};
scope.values.price = 1234;
}
};
});
then use in your html like:
<body ng-controller="MainCtrl">
<p>value {{values.price}}</p>
<my-tree att="{{attValue}}"></my-tree>
</body>
here values.price shown from directive in MainCtrl
Within a controller I have an ajax call that populates $scope.main_data. I want to within a directive get that data when it populates. However the two issues I'm having is:
I cannot seem to access $scope.main_data from the directive.
$scope.watch doesn't seem to work because of this.
How can I print the data from the directive once the data arrives?
CONTROLLER:
app.controller('myCtrl', ['$scope','$http', function($scope, $http) {
$scope.main_data = [];
$http.get("some_url").success( function(data) {
$scope.main_data = data;
});
}]);
DIRECTIVE:
app.directive('myDir', function($compile, $rootScope) {
return {
restrict: 'A',
scope: {
items: '=items'
},
link: function($scope, element, attrs, ctrl) {
$scope.$watch($scope.main_data ,function(newValue,oldValue){
if (newValue){
console.log($scope.main_data);
}
});
}
};
});
HTML:
<div ng-controller="myCtrl">
<div my-dir>
<div>
</div>
The directive in the html exists within the controller but for some reason I can't access the controller scope through $scope but $scope.$parent.
You've almost got it.
app.directive('myDir', function($compile, $rootScope) {
return {
restrict: 'A',
scope: {
items: '=items'
},
link: function($scope, element, attrs, ctrl) {
$scope.$watch("items",function(newValue,oldValue){
if (newValue){
console.log($scope.main_data);
}
});
}
};
});
And html:
<div ng-controller="myCtrl">
<div my-dir items="main_data">
</div>
</div>
Even though you can "hack" around and access main_data using series of$parent calls on your scope and using some other methods, just pass it in to your directive with = binding, so it will be updated when controller scope is updated. In fact you don't even need $watch in this case, you will always have actual data.
html :
<div ng-app="appMod">
<div task-info>{ { data.name } }</div>
</div>
script :
var appmod = angular.module('appMod', []);
appmod.directive("taskInfo", function () {
return {
restrict: 'A',
scope: {},
link: function ($scope, $element, attr) {
$scope.taskdat = '{"name":"Task name","status":"Completed"}';
$scope.data = JSON.parse($scope.taskdat);
scope = $scope; //scope data
},
};
});
is it possible to bind directive scope without having controller scope in Angular Js? If yes, please give me some solution examples.
You don't need a controller scope for writing a directive , see this fiddle.
Here, there is no controller scope, and the value hero is bound within the directive as:
myApp.directive('myDirective', function() {
return {
restrict: 'EAC',
link: function($scope, element, attrs, controller) {
var controllerOptions, options;
$scope.hero='superhero'
}
};
});
Works fine :)
Also the example you provided is similar, but you just need to remove scope from returned JSON object(from directive), as it is being defined as $scope inside the link fucntion.
see : http://jsfiddle.net/bg0L80Lx/
controller option ?
.directive('mydirective', function() {
return {
restrict: 'A', // always required
//controller: 'SomeController'
template:'<b>{{status}}</b>',
controller:'YourCtrl'
}
})
I have a directive that has a local scope where a partial contains ng-click.
The Fiddle is there: http://jsfiddle.net/stephanedeluca/QRZFs/13/
Unfortunatelly, since I moved my code to the directive, ng-click does not fire anymore.
The controller and the directive is as follows:
var app = angular.module('myApp', ['ngSanitize']);
app.directive('plantStages', function ($compile) {
return {
restrict: 'E',
transclude: true,
template: '<figure class="cornStages">\
<p ng-transclude style="color: skyblue"></p>\
<hr/>\
<p ng-bind-html="title"></p>\
<p ng-bind-html="subtitle">{{subtitle}}</p>\
<ul>\
<li ng-repeat="stage in stages" ng-click="changePage(stage)">{{stage}}</li>\
</ul>\
</figure>',
scope: {
stages:"=",
title:'#'
},
link: function (scope, element, attrs, ctrl, transclude) {
if (!attrs.title) scope.title = "Default title";
}
};
});
app.controller('myCtrl', function ($scope, $location, $http) {
$scope.stages = ['floraison', 'montaison'];
$scope.changePage = function (page) {
var url = "corn.page.html#/"+page;
console.log("Change page "+page+" with url "+url);
alert("about to change page as follows: document.location.href = "+url);
};
});
The html that invokes it is as follows:
<div ng-controller="myCtrl">
Stages,
<p ng-repeat="stage in stages">{{stage}}</p>
<hr/>
Plant stages
<plant-stages
title="<b>Exploration<br/>du cycle</b>"
subtitle="<em>This is a<br/>sub title</em>"
stages="stages"
>
Inner<br/>directive
</plant-stages>
</div>
Any idea?
You can't access changePage() defined in controller's scope from directive directly, since your directive has isolated scope. However, there are still several ways to do it:
Option 1:
Option 1 is the most simple option. However it is much like a workaround and I don't recommend to use it widely. You can get your controller's scope from element passed to link function and invoke changePage there:
link: function (scope, element, attrs, ctrl, transclude) {
if (!attrs.title) scope.title = "Default title";
scope.changePage = element.scope().changePage; // <= Get parent scope from element, it will have changePage()
}
Option 2:
If you don't have any logic that involves scope defined in the outer controller (as in your example), you can define inner controller for your directive and perform it there:
app.directive('plantStages', function ($compile) {
return {
...
controller: ['$scope', function($scope) {
$scope.changePage = function(page) {
var url = "corn.page.html#/"+page;
console.log("Change page "+page+" with url "+url);
alert("about to change page as follows: document.location.href = "+url);
}
}]
};
});
Option 3:
If you want do reuse logic defined in changePage() in different directives and controllers, the best way to do it is to move the logic to some service that may be injected to both controller and directive:
app.service('changePageService', function() {
this.changePage = function(page) {
var url = "corn.page.html#/"+page;
console.log("Change page "+page+" with url "+url);
alert("about to change page as follows: document.location.href = "+url);
}
});
app.controller('myCtrl', function ($scope, $location, $http, changePageService) {
...
changePageService.changePage('page');
...
});
app.directive('plantStages', function ($compile) {
...
controller: ['$scope', 'changePageService', function($scope, changePageService) {
$scope.changePage = changePageService.changePage;
}]
...
});
Option 4:
You can pass piece of code like changePage(page) as value of some attribute of the directive and inside directive define scope property with '&' that will create a function that will be executed in the outer controller's scope with arguments passed to that function. Example:
JavaScript
app.directive('plantStages', function ($compile) {
return {
restrict: 'E',
transclude: true,
template: '<figure class="cornStages">\
<p ng-transclude style="color: skyblue"></p>\
<hr/>\
<p ng-bind-html="title"></p>\
<p ng-bind-html="subtitle"></p>\
<ul>\
<li ng-repeat="stage in stages" ng-click="changePage({page: stage})">{{stage}}</li>\
</ul>\
</figure>',
scope: {
stages:"=",
title:'#',
changePage:'&'
},
link: function (scope, element, attrs, ctrl, transclude) {
if (!attrs.title) scope.title = "Default title";
}
};
});
HTML
<div ng-controller="myCtrl">
Stages,
<p ng-repeat="stage in stages">{{stage}}</p>
<hr/>
Plant stages
<plant-stages
title="<b>Exploration<br/>du cycle</b>"
subtitle="<em>This is a<br/>sub title</em>"
stages="stages"
change-page="changePage(page)"
>
Inner<br/>directive
</plant-stages>
Plunker: http://plnkr.co/edit/s4CFI3wxs0SOmZVhUkC4?p=preview
The idea of directives is to treat them as reusable components and avoid external dependencies wherever possible. If you have the possibility to define the behavior of your directive in its own controller then do it.
module.directive('myDirective', function () {
return {
restrict: 'E',
controller: function() { /* behaviour here */ },
template: '<div>Directive Template</div>',
scope: {
/* directive scope */
}
};
});
If this is not possible you can pass the function as explained in the linked question (see comment above). Check the updated fiddle.
I am trying to call a function in a controller, which is part of a custom angular directive, following is the code,
Method 1: (Doesn't work: Controller's function doesn't write to the console)
HTML:
<div ng-app="MyApp">
<my-directive callback-fn="ctrlFn(arg1)"></my-directive>
</div>
JS:
var app = angular.module('MyApp', []);
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: { someCtrlFn: '&callbackFn' },
link: function(scope, element, attrs) {
scope.someCtrlFn({arg1: 22});
},
controller: function ($scope) {
$scope.ctrlFn = function(test) {
console.log(test);
}
}
}
});
When I remove the directive's controller from it and and create a new controller it works,
Method 2: (Works: Controller's function does write to the console)
HTML:
<div ng-app="MyApp" ng-controller="Ctrl">
<my-directive callback-fn="ctrlFn(arg1)"></my-directive>
</div>
JS:
var app = angular.module('MyApp', []);
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: { someCtrlFn: '&callbackFn' },
link: function(scope, element, attrs) {
scope.someCtrlFn({arg1: 22});
}
}
});
app.controller('Ctrl', function ($scope) {
$scope.ctrlFn = function(test) {
console.log(test);
}
});
I would like know how to get the behavior of Method 2 in Method 1 i.e., to be able to call the directive's controller's function from directive's attribute.
Any help is greatly appreciated, Thank you!
In Method 1, you are creating an isolated scope and defining a scope value someCtrlFn that takes in a function from the parent scope that is using your directive. The function to use is specified by the attribute callbackFn.
The way directives work with these scope items is that they are expected to be assigned from things that are on the parent scope that is active when the directive is used. So, if you have a controller Ctrl as in your Method 2, then use the directive within that scope, your directive is trying to match the what you defined in the attribute to what is available on Ctrl's scope.
So, in your first example, it's looking for a function called ctrlFn on the parent scope, but there isn't one. It will not try to look for it on the directive's controller. This is why Method 2 works, because there is a parent scope where ctrlFn is defined, and the directive is able to properly invoke that expression.
The purpose of these scope attributes is to allow directives to bind to values or functions on a parent scope to facilitate communication. For example, to give the directive data that it will display or modify, or allow the parent to define a function the directive can invoke for a callback during an event or what have you. The parent scope cannot move into the directive's scope and force the directive's scope to use its own defined items (unless you set it up so your directive uses a default value or function if the attribute is omitted or whatever).
They are not used so a directive can define things on its scope that it uses internally. If these things are internal to the directive, you can simply add them to the scope during link or whatever is suitable.
Did you mean something like this?
var app = angular.module('MyApp', []);
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: { },
link: function(scope, element, attrs) {
// defines ctrlFn that can be used later by this directive's template or controller
scope.ctrlFn = function(test) {
console.log(test);
}
// immediately invokes ctrlFn to log a message, just here to illustrate
scope.ctrlFn('Hello World!');
}
}
});
Achieved it using $rootScope instead of $scope within the directive's controller
HTML:
<div ng-app="MyApp">
<my-directive callback-fn="ctrlFn(arg1)"></my-directive>
</div>
JS:
<script>
var app = angular.module('MyApp', []);
app.directive('myDirective', function($rootScope) {
return {
restrict: 'E',
scope: { someCtrlFn: '&callbackFn' },
link: function(scope, element, attrs) {
scope.someCtrlFn({arg1: 22});
},
controller: function ($scope) {
$rootScope.ctrlFn = function(test) {
console.log(test);
}
}
}
});
</script>