4th argument to the link function - angularjs

I have a directive written by another developer that basically has following configuration:
{
controller: MyController,
controllerAs: 'myController',
link: function(scope, $element, attrs, ctrl) {
// Does some setup that requires controller
}
}
This works fine, controller is passed as fourth argument, directive works.
Now I decided to make the directive more flexible, reusable and stuff. So, to directive configuration I added
require: '?ngModel'
Suddenly, now my controller is never passed to a link function. There is no array for the fourth argument, there's no fifth argument, nada.
I tried adding controller to require directive - but it still does not find it.
How do I add require and pass the controller?

require means that the directive you required (ngModelController in this case) will have its controller sent as the fourth argument of the linking function. The default controller is the directive's, whose linking function is called, controller but requiring another directives overrides it (even if it's an optional requirement and the required directive isn't present, in which case the fourth argument will be undefined). Fortunately, require can be an array of directives, so this will work:
module.directive('myDirective', function() {
return {
controller: MyController,
controllerAs: 'myController',
require: ['myDirective', '?ngModel'],
link: function(scope, $element, attrs, controllers) {
var MyDirectiveController = controllers[0]; //this directive's controller
var ngModelController = controllers[1];
}
};
});
PLUNKER

Related

Which function will execute first in angularjs directives? link or controller?

I have gone through this link https://www.undefinednull.com/2014/07/07/practical-guide-to-prelink-postlink-and-controller-methods-of-angular-directives/, they said the order(first to last) of execution of link and controller is
Controller,
Pre-Link function,
Post-Link function
But here I read AngularJS: What is the need of the directive's link function when we already had directive's controller with scope? link executes before the controller. Which one I should believe in?
If it was first link then controller it wouldn't be possible to require other directives and and use theirs controllers in link function.
Take a look at the code from documentation:
var directiveDefinitionObject = {
controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
controllerAs: 'stringIdentifier',
require: 'siblingDirectiveName', // requiring another directive
compile: function compile(tElement, tAttrs, transclude) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) { ... }, //siblingDirectiveName's controller is available in link function
post: function postLink(scope, iElement, iAttrs, controller) { ... }
}
},
};
return directiveDefinitionObject;
});
To support this statement we can read o the same page :
controller
Controller constructor function. The controller is instantiated before the pre-linking phase and can be accessed by other directives (see require attribute). This allows the directives to communicate with each other and augment each other's behavior.

Angular directive manual compile in link function + transclude

I'm in need of doing transclusion together with manual compilation in link function and I'm wondering if there is a better way than what I'm pasting below as "Dynamic". It seems to be working but I'm not sure what are the consequences of removing ng-transclude attribute and I'm removing it due to Angular complaining to Error: [ngTransclude:orphan] Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found. Element: <div ng-transclude="">.
Bottom-line is that I need to provide different templates for different instances of this directive with fallback to generic ones.
I can't use compile part. Here I have just simple example but I have also other cases with big link function that I would rather don't want to convert as this should be just "golden bullet" type of adjustment.
This little guy can be found here https://github.com/institut-de-genomique/Ultimate-DataTable/blob/master/src/directives/udt-form.js in original form.
Original
angular.module('ultimateDataTableServices')
.directive('udtForm', function () {
return {
restrict: 'A',
replace:true,
transclude:true,
templateUrl:'udt-form.html',
link: function(scope, element, attr) {
}
};
});
Dynamic
angular.module('ultimateDataTableServices')
.directive('udtForm', ['$compile', '$templateCache', function ($compile, $templateCache) {
return {
restrict: 'A',
transclude:true,
link: function(scope, element, attr, ctrls, transclude) {
var tplName = 'udt-form.html',
tplPath = scope.udtTable.configMaster.templates[tplName] || scope.udtTable.configMaster.templatesMaster[tplName] || tplName,
elem = angular.element($templateCache.get(tplPath));
elem.find('[ng-transclude]').removeAttr('ng-transclude').append(transclude());
element.replaceWith($compile($templateCache.get(tplPath))(scope));
}
};
}]);
TIA!

Unable to share controller instance via require in angular directive

I have a confirm box which I want to attach another directive to called confirmBoxToggle, but I'm unable to share the same controller instance in order for it to work. I've looked at multiple examples and also read the docs to see if I'm doing something crazy, but the only thing I can see is that I don't declare my controller inside the directive but rather giving a reference to it. But I can't see this being the issue.
I get this error when doing this:
Controller 'confirmBox', required by directive 'confirmBoxToggle', can't be found!
What am I doing wrong?
The box directive:
core.directive('confirmBox', [function() {
return {
scope: {},
controller: 'ConfirmBoxCtrl',
controllerAs: 'confirmBox',
templateUrl: 'app/views/components/core/confirmation-box.html',
link: function(scope, element, attrs, ctrl) {
}
};
}]);
The toggle directive:
core.directive('confirmBoxToggle', [function() {
return {
scope: {},
require: '^confirmBox',
link: function(scope, element, attrs, ctrl) {
element.on('click', function() {
ctrl.toggleBox();
});
}
};
}]);
The controller for both directives:
core.controller('ConfirmBoxCtrl', [function() {
var confirmBox = this;
confirmBox.toggleBox = function() {
confirmBox.isActive = !confirmBox.isActive;
};
}]);
I use the directives like this:
<confirm-box></confirm-box>
<span confirm-box-toggle>Delete</span>
Controller confirm or confirmBox can't be found?
Do you use that controller elsewhere, and does it work on it's own?
Basically you used require: '^confirmBox' that means while using confirmBoxToggle directive, it must be wrap with confirmBoxdirective(should be there in parent element as ^) so that you could access to the confirmBox link function 4th parameter.
HTML
<confirm-box>
<span confirm-box-toggle>Delete</span>
</confirm-box>
Also you can't have templateUrl inside your confirmBox directive, which will replace your <span confirm-box-toggle>Delete</span> html by the template loaded form templateUrl.
Demo Plunkr

Using required controller in controller - not link

When using require: "^directive1" in directive2 You can access it's variables and function in link function link: function (scope, element, attrs, ctrl) {} - ctrl is directive's 1 controller.
How to use ctrl in controller - not link func?
Is there anything that can be shared between the controllers? Yes, there is: the directives can share the same scope!
You need to expose the controller on the shared scope object. The simplest way to do that is in the link function of your second directive:
var linkFn = function (scope, element, attrs, ctrl) {
scope.exposedCtrl = ctrl;
};
As long as you're not using isolated scopes, you should be able to access $scope.exposedCtrl in your parent directive. Just remember that the exposedCtrl property won't be initialized until the child directive's link function has been called.
I found perfect solution for my issue:
.controller('myCtrl', ['$element', function($element) {
var parentCtrl = $element.parent().controller('diective1');
}])
source: https://github.com/angular/angular.js/issues/10998#issuecomment-73308256

Why this syntax for link function inside directive is wrong?

I have wasted a lot of time to figure out why my directive link function is not working:
In directive code you can write this line for controller:
controller: ['$scope', '$element', function($scope, $element) {
}],
But you CAN NOT write the same for link, like:
link: ['scope', 'element', 'attrs', function(scope, element, attrs) {}]
Only this will work:
link: function(scope, element, attrs) {}
Why is that?
Because controller is an injectable function, whereas link is not. It's a traditional function where the order of arguments matter, and not their name. It accepts, in order
the scope of the directive,
the element of the directive
the attributes of this element
the controller
a transclude function
This is documented: https://docs.angularjs.org/api/ng/service/$compile#-link-

Resources