I have an attribute directive and I'd like to expose an API on that directive to be called on the controller.
I followed the top answer from this question but this no longer seems to work anymore, or at least it doesn't work for me. Since it's been almost three years, how can I do this now?
In my "myDirective" directive I have:
return {
restrict: 'A',
scope: {
api: '='
},
link: function(scope, element){
scope.api = {
someFunction: function(){...}
}
...
}
In my markup I have:
<div myDirective api="b"></div>
And finally in my controller, I try to call:
$scope.b.someFunction() --> undefined is not an object
The way to do so, is to create a service that is used as an API for both the directive and the controller.
Inject the service into your directive controller to use it in the directive, and inject it into the controller.
All the API logic should be in the service, so calling the functions from either the directive or the controller will have the same result.
The question title explain my problem i want to send data from a controller to directive so i can use the data in the directive controller or view.
Here is the controller code:
$scope.following = product.vendorId.isUserFollowing;
In the controller view:
<vas-follow following="{{following}}"></vas-follow>
following the property am trying to pass to the directive, the directive code:
.directive('vasFollow', vasFollow);
function vasFollow() {
var directive = {
restrict: "EA",
scope: {
following: '#'
},
link: link,
controller: vasFollowCtrl,
templateUrl: 'templates/directives/vasFollow.html',
};
return directive;
function link(scope, element, attrs) {
/* */
};
}
I tried first to use the following like so {{following}} in the directive view but it's not passing, also it is undefined in the directive controller.
I have read a lot of slimier issues but, i couldn't conclude why am having this problem.
Use ng-model for directive instead of your own replacement for it
Remove {{}} from assignment to share link to variable instead of just evaluated value
And please, use div or common DOM element instead of exact naming directive - it have side-effects in I.E.
Say I want to generate some Html on the fly but based on the same definition in my directive, how would I achieve this?
My directive is currently expecting JSON:
app.directive('myDirective', [function () {
return {
template: '<div>A far more impressive template lives here...</div>',
restrict: 'AE',
scope: {
eventData: '='
},
link: function ($scope, $element, $attrs) {
//some cool functions...
}
};
}]);
Within my ng-repeat element, I have this kinda thing, where the event-data contains the named scope item from the ng-repeat, clause.
<div ng-repeat="event in myCtrl.events">
<my-directive event-data="event"></my-directive>
</div>
So how do I go about manually passing in some JSON to use as the data attribute so I can then generate fragments elsewhere, e.g. outside a scoped ng-repeat?
What I am hoping to do is add a function to a controller of mine which just returns a new instance of my directive and then I can plant that somewhere else (I am actually hoping to use as the content of a Google Maps info window).
I hope I have made some sort of sense here!
Thanks
Why do we "require" ngModel in a directive instead of in the directive injection arguments?
angular.module('customControl', ['ngSanitize']).
directive('contenteditable', ['$sce', function($sce) {
return {
restrict: 'A', // only activate on element attribute
require: '?ngModel',
Directives aren't injected. They are placed on elements which means that you need a specific instance in your custom directive.
requireing another directive on your own custom directive is how you tell Angular that to use your directive on an element, another directive is needed for things to work properly.
When you create a directive, you can put code into the compiler, the link function or the controller.
In the docs, they explain that:
compile and link function are used in different phases of the angular
cycle
controllers are shared between directives
However, for me it is not clear, which kind of code should go where.
E.g.: Can I create functions in compile and have them attached to the scope in link or only attach functions to the scope in the controller?
How are controllers shared between directives, if each directive can have its own controller? Are the controllers really shared or is it just the scope properties?
Compile :
This is the phase where Angular actually compiles your directive. This compile function is called just once for each references to the given directive. For example, say you are using the ng-repeat directive. ng-repeat will have to look up the element it is attached to, extract the html fragment that it is attached to and create a template function.
If you have used HandleBars, underscore templates or equivalent, its like compiling their templates to extract out a template function. To this template function you pass data and the return value of that function is the html with the data in the right places.
The compilation phase is that step in Angular which returns the template function. This template function in angular is called the linking function.
Linking phase :
The linking phase is where you attach the data ( $scope ) to the linking function and it should return you the linked html. Since the directive also specifies where this html goes or what it changes, it is already good to go. This is the function where you want to make changes to the linked html, i.e the html that already has the data attached to it. In angular if you write code in the linking function its generally the post-link function (by default). It is kind of a callback that gets called after the linking function has linked the data with the template.
Controller :
The controller is a place where you put in some directive specific logic. This logic can go into the linking function as well, but then you would have to put that logic on the scope to make it "shareable". The problem with that is that you would then be corrupting the scope with your directives stuff which is not really something that is expected.
So what is the alternative if two Directives want to talk to each other / co-operate with each other? Ofcourse you could put all that logic into a service and then make both these directives depend on that service but that just brings in one more dependency. The alternative is to provide a Controller for this scope ( usually isolate scope ? ) and then this controller is injected into another directive when that directive "requires" the other one. See tabs and panes on the first page of angularjs.org for an example.
I wanted to add also what the O'Reily AngularJS book by the Google Team has to say:
Controller - Create a controller which publishes an API for communicating across directives. A good example is Directive to Directive Communication
Link - Programmatically modify resulting DOM element instances, add event listeners, and set up data binding.
Compile - Programmatically modify the DOM template for features across copies of a directive, as when used in ng-repeat. Your compile function can also return link functions to modify the resulting element instances.
A directive allows you to extend the HTML vocabulary in a declarative fashion for building web components. The ng-app attribute is a directive, so is ng-controller and all of the ng- prefixed attributes. Directives can be attributes, tags or even class names, comments.
How directives are born (compilation and instantiation)
Compile: We’ll use the compile function to both manipulate the DOM before it’s rendered and return a link function (that will handle the linking for us). This also is the place to put any methods that need to be shared around with all of the instances of this directive.
link: We’ll use the link function to register all listeners on a specific DOM element (that’s cloned from the template) and set up our bindings to the page.
If set in the compile() function they would only have been set once (which is often what you want). If set in the link() function they would be set every time the HTML element is bound to data in the
object.
<div ng-repeat="i in [0,1,2]">
<simple>
<div>Inner content</div>
</simple>
</div>
app.directive("simple", function(){
return {
restrict: "EA",
transclude:true,
template:"<div>{{label}}<div ng-transclude></div></div>",
compile: function(element, attributes){
return {
pre: function(scope, element, attributes, controller, transcludeFn){
},
post: function(scope, element, attributes, controller, transcludeFn){
}
}
},
controller: function($scope){
}
};
});
Compile function returns the pre and post link function. In the pre link function we have the instance template and also the scope from the controller, but yet the template is not bound to scope and still don't have transcluded content.
Post link function is where post link is the last function to execute. Now the transclusion is complete, the template is linked to a scope, and the view will update with data bound values after the next digest cycle. The link option is just a shortcut to setting up a post-link function.
controller: The directive controller can be passed to another directive linking/compiling phase. It can be injected into other directices as a mean to use in inter-directive communication.
You have to specify the name of the directive to be required – It should be bound to same element or its parent. The name can be prefixed with:
? – Will not raise any error if a mentioned directive does not exist.
^ – Will look for the directive on parent elements, if not available on the same element.
Use square bracket [‘directive1′, ‘directive2′, ‘directive3′] to require multiple directives controller.
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope, $element) {
});
app.directive('parentDirective', function() {
return {
restrict: 'E',
template: '<child-directive></child-directive>',
controller: function($scope, $element){
this.variable = "Hi Vinothbabu"
}
}
});
app.directive('childDirective', function() {
return {
restrict: 'E',
template: '<h1>I am child</h1>',
replace: true,
require: '^parentDirective',
link: function($scope, $element, attr, parentDirectCtrl){
//you now have access to parentDirectCtrl.variable
}
}
});
Also, a good reason to use a controller vs. link function (since they both have access to the scope, element, and attrs) is because you can pass in any available service or dependency into a controller (and in any order), whereas you cannot do that with the link function. Notice the different signatures:
controller: function($scope, $exceptionHandler, $attr, $element, $parse, $myOtherService, someCrazyDependency) {...
vs.
link: function(scope, element, attrs) {... //no services allowed
this is a good sample for understand directive phases
http://codepen.io/anon/pen/oXMdBQ?editors=101
var app = angular.module('myapp', [])
app.directive('slngStylePrelink', function() {
return {
scope: {
drctvName: '#'
},
controller: function($scope) {
console.log('controller for ', $scope.drctvName);
},
compile: function(element, attr) {
console.log("compile for ", attr.name)
return {
post: function($scope, element, attr) {
console.log('post link for ', attr.name)
},
pre: function($scope, element, attr) {
$scope.element = element;
console.log('pre link for ', attr.name)
// from angular.js 1.4.1
function ngStyleWatchAction(newStyles, oldStyles) {
if (oldStyles && (newStyles !== oldStyles)) {
forEach(oldStyles, function(val, style) {
element.css(style, '');
});
}
if (newStyles) element.css(newStyles);
}
$scope.$watch(attr.slngStylePrelink, ngStyleWatchAction, true);
// Run immediately, because the watcher's first run is async
ngStyleWatchAction($scope.$eval(attr.slngStylePrelink));
}
};
}
};
});
html
<body ng-app="myapp">
<div slng-style-prelink="{height:'500px'}" drctv-name='parent' style="border:1px solid" name="parent">
<div slng-style-prelink="{height:'50%'}" drctv-name='child' style="border:1px solid red" name='child'>
</div>
</div>
</body>
compile: used when we need to modify directive template, like add new expression, append another directive inside this directive
controller: used when we need to share/reuse $scope data
link: it is a function which used when we need to attach event handler or to manipulate DOM.