Using required controller in controller - not link - angularjs

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

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.

How do you dynamically change a directives controller in angular.js

I have my directive and I want to change the controller and html of a small section of the page onclick of a submit button. Changeing the HTML is working but changing the controller is not.
The line: attrs.ngController = "wordlistsPageController";
is not working. Please let me know what I can do to dynamically set the controller since this does not work.
It is a little web app game, so I do not want to change the whole page, just the game section between 3 game pages.
Here is my directive:
myApp.directive('gamePanel', function ($compile) {
return {
restrict: 'E',
templateUrl: "templates/wordlistPage.ejs",
link : function(scope, elem, attrs) {
attrs.ngController = "wordlistsPageController";//NOT WORKING
scope.submitWordlist = function() {
//change html
elem.html('<ng-include src="\'templates/gameOptionsDialogPage.ejs\'"></ng-include>');
$compile(elem.contents())(scope);
//change controller
attrs.ngController = "optionsPageController";///NOT WORKING
};
scope.backToWordlist = function() {
elem.html('<ng-include src="\'templates/wordlistPage.ejs\'"></ng-include>');
$compile(elem.contents())(scope);
attrs.ngController = "wordlistsPageController";///NOT WORKING
};
}//end link
}
});//end directive
How to Dynamically Change a Directive Controller
To instantiate different controllers inside a directive linking function, use the $controller service.
angular.module("myApp").directive('gamePanel', function ($controller) {
return {
restrict: 'E',
template: "<p>Game Panel Template</p>",
//controller: "pageController as vm",
link : function(scope, elem, attrs, ctrl, transclude) {
var locals = { $scope: scope,
$element: elem,
$attrs: attrs,
$transclude, transclude
};
scope.vm = $controller("pageController", locals);
}//end link
}
});
The above example instantiates a controller and puts it on scope as vm.
Best practice
The injected locals should follow the conventions of the locals provided by the $compile service. For more information on $compile service injected locals, see AngularJS $compile service API Reference API -- controller.
Beware: memory leaks
When destroying scopes, all watchers created on those scopes should be de-registered. This can be done by invoking scope.$destroy().
When destroying controllers, all watchers created by the controller should be de-registered. This can be done by invoking the de-register function returned by each $watch.

4th argument to the link function

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

How to import ngModel into link funciton of directive

I have to access ngModel into my directives link function. Here is the code:
app.directive("contenteditable", function() {
var directive = {};
directive.require = ['^parentDirective','?ngModel'];
directive.link = function(scope, element, attrs, ngModel) {
ngModel.someMethod(); // Gives method not found
}
But I am not sure how to pass or access 'ngModel' from directive link function.
Since you're "requiring" an array of controllers, you will get an array of controllers back injected into your link function.
directive.link = function(scope, element, attrs, ctrls) {
var ngModel = ctrls[1];
ngModel.someMethod();
}
From documentation:
The require takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the injected argument will be an array in corresponding order.

How to create a new isolated scope with an existing object?

I want to create a new scope with this object:
$scope.model = {
itemA: "First item",
itemB: "Second item"
};
// I know, this is wrong, but I want to show you, what I would like to do.
var newScope = $scope.$new($scope.model);
The new scope I want to access in the ngTransclude-Part of my directive:
link: function (scope, element, attrs, ctrl, transclude) {
transclude(scope.model, function (clone, scope) {
element.find('section').html("").append(clone);
});
And in the template:
<p>{{itemA}} - {{itemB}}
But this doesn´t work
I have the idea from: http://angular-tips.com/blog/2014/03/transclusion-and-scopes/
but I don´t want to work in the scope of the directive, but in a new scope.
AFAIK when you are creating directive usually it inherits the scope. The idea is to create isolated scope and this is done by doing.
.directive('directiveName', function ($compile) {
return {
restrict: "AE",
scope:{
yourModelName:"=";
}
link: function (scope, element) {
var el = angular.element('<div>Here you put your template scope.yourModelName</div>');
$compile(el)(scope);
element.append(el);
}
};
})
Will be copied from the upper scope but you can change it in the directive without changing it in the upper scope
his is my solution I found to a similar problem. Slightly long-winded but it works!
Create a new, 'empty' class directive with its own scope.
Add this directive as a class attribute to your DOM element. It automatically takes the scope of the new directive.
In your case, you would use it on your p tag. You would then select this element in your link function and call scope() on it:
1. <p id="ID" class="my-empty-directive">{{itemA}} - {{itemB}}
2. create your new directive:
angular.module('sgComponents').directive('panelData', [function () {
return {
restrict: 'C', // a class Directive
scope: true // with its own scope
};
}]);
2. link: function (scope, element, attrs, ctrl, transclude) {
var pTag = jQuery('#ID');
var angularElement = angular.element(pTag);
var newScope = angularElement.scope(); // THIS WILL BE THE NEW EMPTY DIRECTIVE'S SCOPE

Resources