Using current controller in directive with inheritence - angularjs

I have 3 directives which require themselves this way:
.directive('grandParent', function () {
return {
retrict: 'E',
controller: function ($scope, $element, $attrs) {
this.foo = function () {
};
},
link: function(scope, element, attrs, ctrl) {
// ...
}
}
})
.directive('parent', function () {
return {
retrict: 'E',
require: ['parent', '^grandParent'],
controller: function ($scope, $element, $attrs) {
this.foo = function () {
};
},
link: function(scope, element, attrs, ctrls) {
var parentCtrl = ctrls[0],
grandParentCtrl = ctrls[1];
// ...
}
}
})
.directive('child', function () {
return {
retrict: 'E',
require: ['child', '^parent', '^grandParent'],
controller: function ($scope, $element, $attrs) {
this.foo = function () {
};
},
link: function(scope, element, attrs, ctrls) {
var childCtrl = ctrls[0],
parentCtrl = ctrls[1],
grandParentCtrl = ctrls[2];
// ...
}
}
})
I need in the child to use parent and grand parent controller functions, that why I didn't populate their link function with the business code.
I didn't find anywhere this kind of call of current directive controller.
I'm wondering if there is any problem with this kind of practice (circular reference...) or if it's a bad practice and there are other way to do that.

Related

AngularJS directive with require and 2 controllers

Is it possible to have a parent directive and a child directive, both with their own controller?
Something like:
.controller('ParentController', function () {
var self = this;
self.action = function () {
console.log('parent action');
}
})
.controller('TestSomethingController', function () {
var self = this;
self.something = function () {
console.log('something');
}
})
.directive('parent', function () {
return {
restrict: 'A',
controller: 'ParentController',
link: function (scope, element, attrs, controller) {
controller.action();
}
}
})
.directive('test', function () {
return {
restrict: 'A',
require: 'parent',
controller: 'TestSomethingController',
link: function (scope, element, attrs, controller) {
controller.something();
}
};
});
I tried to do this one codepen like this:
http://codepen.io/r3plica/pen/bdygeP?editors=101
If I remove the require, it obviously works, but I would like to keep the require.
Does anyone know if that is possible?
You can require multiple directives. Have it require itself as well as parent. With this syntax, the last parameter of link will be an array of the controllers of the given directives.
.directive('test', function () {
return {
restrict: 'A',
require: ['parent', 'test'],
controller: 'TestSomethingController',
link: function (scope, element, attrs, controllers) {
controllers[0].action(); // parent
controllers[1].something(); // self
}
};
});
Here is a forked, working version of your CodePen.

How can I pass variables from an isolated scope directive to a child?

I have two isolated scope directives. Ideally I like both to work independently and not require any custom templates. The first directive is going to be page scroll watcher, when it hits a certain point I want it to trigger an update in the other directive. Is it possible for a child directive to watch a variable in the parent directive?
I've created a simple plunkr to illustrate the issue, http://plnkr.co/edit/wwfBzmemyrj1r1R54riM?p=preview
/*
<div ng-outer>Outer directive {{myvar}}
<div ng-inner="myvar">Inner directive</div>
</div>
*/
app.directive('ngOuter', [ '$timeout', function ($timeout) {
var directive = {
restrict: 'A'
,scope:{}
}
directive.link = function (scope, element, attrs) {
$timeout(function(){
scope.myvar = "test 001"
},1000)
}
return directive;
}]);
app.directive('ngInner', [ function () {
var directive = {
restrict: 'A'
,scope:{ data: '=ngInner', myvar: '=myvar' }
}
directive.link = function (scope, element, attrs) {
scope.$watch('data', function(newVal, oldVal){
if(newVal)
element.text("new inner val", newVal);
});
scope.$watch('myvar', function(newVal, oldVal){
if(newVal)
element.text("new myvar", newVal);
});
}
return directive;
}]);
Solved this issue by using
angular.element(element.parent()).isolateScope();
The child directive can access the scope of the parent directive and watch variables etc.
http://plnkr.co/edit/RAO6q81ZE4tClMDMiLFb?p=preview
First approach of passing variable from parent directive to the child is by using an 'attr' in child scope with '#' and initialize it in parent controller as following:
var app = angular.module('app', []);
app.directive('ngOuter', [ '$timeout', function ($timeout) {
return{
restrict: 'E',
scope:{},
template: '<ng-inner attr="{{myvar}}">Inner directive {{myvar}}</ng-inner>',
controller: ['$scope', function($scope) {
$scope.myvar = "test 001";
}],
link: function (scope, elem, attrs) {
}
}
}]);
app.directive('ngInner', [ function () {
return{
restrict: 'E',
scope:{'attr' : "#"},
link: function (scope, element, attrs) {
console.log(scope.attr);
}
}
}]);
html:
<ng-outer></ng-outer>
2nd approach is utilizing a function in a parent controller which returns the "myvar" value and call that function in child directive:
app.directive('ngOuter', [ '$timeout', function ($timeout) {
return{
restrict: 'E',
scope:{},
template: '<ng-inner attr="{{myvar}}">Inner directive {{myvar}}</ng-inner>',
controller: ['$scope', function($scope) {
$scope.myvar = "test 001";
this.getMyvar = function() {
return $scope.myvar;
};
}],
link: function (scope, elem, attrs) {
}
}
}]);
app.directive('ngInner', [ function () {
return{
restrict: 'E',
require: '^ngOuter',
scope:{'attr' : "#"},
link: function (scope, element, attrs, parentDirCtrl) {
console.log(parentDirCtrl.getMyvar());
}
}
}]);
3rd Approach: you can inject a service to both inner and outer directives.Then use the service.
var app = angular.module('app', []);
app.service('myService', [
function() {
var service = {
myvar: 'test001',
setMyVar: function(value) {
this.myvar = value;
},
getMyVar: function() {
return this.myvar;
}
}
return service;
}
]);
app.directive('ngOuter', ['$timeout', 'myService',
function($timeout, myService) {
var directive = {
restrict: 'A',
scope: {}
}
directive.link = function(scope, element, attrs) {
$timeout(function() {
scope.myvar = myService.getMyVar();
}, 1000)
}
return directive;
}
]);
app.directive('ngInner', ['myService',
function(myService) {
var directive = {
restrict: 'A',
scope: {}
}
directive.link = function(scope, element, attrs) {
var variable = myService.getMyVar();
console.log("myvar", variable);
}
return directive;
}
]);

Link function not called

I have a directive defined as following -
.directive('codesection', ['$compile', function ($compile) {
return {
restrict: 'E',
scope: { current: '=', parent: '=', index: '=', params: '=' },
controller: ['Messages', '$scope', 'Modals', 'framewidth', '$http', '$rootScope', function (Messages, $scope, Modals, framewidth, $http, $rootScope) {
//code
}],
link: function (scope, element, attr) {
element.bind('mouseover', function (ev) {
ev.stopPropagation();
var wrappers = angular.element(document.getElementsByClassName('codesection'));
angular.forEach(wrappers, function (value, key) {
angular.element(value).children('span').removeClass('br');
});
element.children('.codesection').children('span').addClass('br');
});
},
compile: function (tElement, tAttr, transclude) {
var contents = tElement.contents().remove();
var compiledContents;
return function (scope, iElement, iAttr) {
if (!compiledContents) {
compiledContents = $compile(contents, transclude);
}
compiledContents(scope, function (clone, scope) {
iElement.append(clone);
});
};
},
templateUrl: './partials/directives/codesection.html',
replace: true
}
}])
The issue I am having is that Link functions is never called. Thank you!
P.S. The reason for the Compile logic is that the directive is recursive.
If you mean that link: function (scope, element, attr) { isn't called then it's pretty clear: The compile function already returns a link function. What is defined as link: doesn't matter anymore and is ignored.

AngularJS directive use required controller in controller

Say I have the following two directives that work with each other
directive('parent', function() {
return {
scope: {},
require: 'ngModel',
controller: function($scope) {
this.doSomething = function() {
//How to use ngModelController here?
};
},
link: function(scope, element, attr, ngModelController) {
}
};
});
directive('child', function() {
return {
scope: {},
require: '^parent',
link: function(scope, element, attr, parent) {
parent.doSomething();
}
};
});
How can I use the ngModelController from within the controller of parent? I suppose I could do scope.ngModelController = ngModelController inside the link function but that seems hacky. Is there a better solution?
Is this any less hacky?
directive('parent', function() {
var ngModelCtrl = null;
return {
scope: {},
require: 'ngModel',
controller: function($scope) {
this.doSomething = function() {
//How to use ngModelController here?
if (ngModelCtrl) {
}
};
},
link: function(scope, element, attr, ngModelController) {
ngModelCtrl = ngModelController;
}
};
});

How would I go about adding directives to an existing directive programmatically whilst ensuring that they have access to the same model?

I have attempted the following which seems to generate the correct html, but doesn't perform the data binding i.e. the new directives that I add do not have access to the $modelValue in ngController:
.controller('MyController', ['$scope', function($scope) {
$scope.aModel = {"key": "value"}
function init() {
$scope.aModel = {"key": "value"}
}
}])
.directive('innerDirective', [function (){
return {
restrict: 'AE',
require: 'ngModel',
template: '<ul></ul>',
link: function(scope, elem, attr, ngModelController) {
console.log(ngModelController.$modelValue) // Doesnt work
}
}
}])
.directive('outerDirective', ["$log", "$compile", function($log, $compile) {
return {
restrict: 'AE',
require: 'ngModel',
scope: {},
template: '<div></div>',
link: function(scope, elem, attrs, ngModelController) {
ngModelController.$render = function() {
var aModel = ngModelController.$modelValue; // Works
var modelName = attrs['ngModel']; // aModel
var html = '<inner-directive ng-model="' + modelName + '"></inner-directive>';
var angularElement = angular.element(html);
elem.append(angularElement);
$compile(angularElement)(scope);
}
}
};
}])
HTML looks like:
<div ng-controller="MyController">
<outer-directive ng-model="aModel"></outer-directive>
</div>
This is based on reccomendations from another question: Adding ngModel to input with a directive
What is it that I'm doing wrong here?
Cheers.
There is two issue:
1- outerDirective is using isolated scope. so aModel value is undefinded in the directive scope.
2-You can't access ngModelController.$modelValue in link function immediately. Look at outerDirective that you access it in the render function.
So you can change your directives as follows:
var app = angular.module('app', []);
app.controller('MyController', ['$scope', function ($scope) {
$scope.aModel = { "key": "value" }
function init() {
$scope.aModel = { "key": "value" }
}
}]);
app.directive('innerDirective', ['$timeout', function ($timeout) {
return {
restrict: 'AE',
require: 'ngModel',
template: '<ul></ul>',
link: function (scope, elem, attr, ngModelController) {
$timeout(function () {
console.log(ngModelController.$modelValue) // It works
});
}
}
}]);
app.directive('outerDirective', ["$log", "$compile", function ($log, $compile) {
return {
restrict: 'AE',
require: 'ngModel',
template: '<div></div>',
link: function (scope, elem, attrs, ngModelController) {
ngModelController.$render = function () {
var aModel = ngModelController.$modelValue; // Works
var modelName = attrs['ngModel']; // aModel
var html = '<inner-directive ng-model="' + modelName + '"></inner-directive>';
var angularElement = angular.element(html);
elem.append(angularElement);
$compile(angularElement)(scope);
}
}
};
}]);

Resources