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.
Related
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.
I try to use Flexisel with Angular but it fails to work somehow.
Here's plnkr link
var app = angular.module('angular.controls.flexSlider', [])
app.directive('flexCarousel', function () {
return {
restrict: 'E',
link: function (scope, element, attrs) {
var options = scope.$eval($(element).attr('data-options'));
console.log(options);
$(element).flexisel(options);
}
};
});
I have forked your plunk, please check the fixes there. http://plnkr.co/edit/zH4u3MwkD6HX39I8PFEr?p=preview
You need a template for your directive in first case:
var app = angular.module('angular.controls.flexSlider', [])
app.directive('flexCarousel', function () {
return {
restrict: 'E',
transclude : true,
template : "<ng-transclude></ng-transclude>",
scope : {
options : "="
},
link: function (scope, element, attrs) {
$('#flexisel').flexisel(scope.options);
}
};
});
app.directive('mainCtrl', function () {
return {
controller: function () {
this.funcA = function(){}
}
};
});
app.directive('addProduct', function () {
return {
restrict: 'E',
require: '^mainCtrl',
link: function (scope, lElement, attrs, mainCtrl) {
mainCtrl.funcA()
}
};
});
I don't want to use the link method but the controller method.
Is there a way to get the mainCtrl in the controller method of the directive addProduct.
something like:
app.directive('addProduct', function () {
return {
restrict: 'E',
require: '^mainCtrl',
controller: function (scope, mainCtrl) {
mainCtrl.funcA()
}
};
});
You'd still need to use the link function because the controllers are injected there. What you could, however, is request your directive's own controller and then set the other required controller as its property:
app.directive('addProduct', function () {
return {
restrict: 'E',
require: ['addProduct','^mainCtrl'],
controller: function ($scope) {
// this.mainCtrl is still not set here
// this.mainCtrl.funcA(); // this will cause an error
// but typically it is invoked in response to some event or function call
$scope.doFuncA = function(){
this.mainCtrl.funcA();
}
},
link: function(scope, element, attrs, ctrls){
var me = ctrls[0], mainCtrl = ctrls[1];
me.mainCtrl = mainCtrl;
}
};
});
Since AngularJS 1.5, you can use the $onInit lifecycle hook of the controller. As written in the documentation of require, when defining require as an object and setting bindToController to true, the required controllers are added to the controller as properties after the controller has been constructed, but before the $onInit method is run. So the code would look like this:
app.directive('mainCtrl', function () {
return {
controller: function () {
this.funcA = function(){}
}
};
});
app.directive('addProduct', function () {
return {
restrict: 'E',
require: {
myParentController: '^mainCtrl'
},
bindToController: true,
controller: function ($scope) {
this.$onInit = function() {
this.myParentController.funcA();
};
}
};
});
Here is my solution:
app.directive('mainCtrl', function () {
return {
controllerAs: 'main',
controller: function () {
this.funcA = function(){}
}
};
});
app.directive('addProduct', function () {
return {
restrict: 'E',
require: '^mainCtrl',
controller: function ($scope) {
$scope.main.funcA();
}
};
});
Pass the controller to the scope on the link function then accessing the scope on controller. Like this:
app.directive('mainCtrl', function () {
return {
controller: function () {
this.funcA = function(){}
}
};
});
app.directive('addProduct', function () {
return {
restrict: 'E',
require: '^mainCtrl',
link: function (scope, lElement, attrs, mainCtrl) {
scope.ctrl=mainCtrl;
},controller:function($scope){
$scope.ctrl.funcA();
}
};
});
Can a directive require itself? Here's an example:
app.directive('menu', function () {
return {
restrict: 'E',
require: '?^menu',
link: function(scope, element, attrs, ctrl) {
console.log('ctrl: ', ctrl);
if (ctrl) {
element.addClass('nested');
} else {
element.addClass('notnested');
}
}
};
});
In my test it doesn't seem to work (ctrl is always undefined). See the plunk
BTW, after this question was answered I discovered that in this case the caret (^) has no effect and the controller passed to the link function is always the instance's own controller. [ plunk ]
You should directly define controller function to expose directive API to other directives:
app.directive('menu', function () {
return {
restrict: 'E',
require: '?^menu',
controller: function($scope){ },
link: function(scope, element, attrs, ctrl) {
console.log('ctrl: ', ctrl);
if (ctrl) {
element.addClass('nested');
} else {
element.addClass('notnested');
}
}
};
});
See http://plnkr.co/edit/cKFuS1lET56VOOYD5rrd?p=preview
With angular 1.4x, you actually can now limit the require statement to parent elements only and exclude the element itself. If you change
require: '?^menu' to require: '?^^menu' (notice the second caret) so that you get
app.directive('menu', function () {
return {
restrict: 'E',
require: '?^^menu',
controller: function($scope){ },
link: function(scope, element, attrs, ctrl) {
console.log('ctrl: ', ctrl);
if (ctrl) {
element.addClass('nested');
} else {
element.addClass('notnested');
}
}
};
});
the code now works as expected.
See http://plnkr.co/edit/2uDUO0LcgDX7xEuBtsJ2?p=preview
I guess here the problem is not with the directive referencing itself. The directive will not know which controller to refer to until specified or defined. To access a controller either it has to be defined or referenced in the directive as below.
app.directive('menu', function () {
return {
restrict: 'E',
controller: 'MainCtrl',
require: ['?^menu'],
link: function(scope, element, attrs, ctrl) {
console.log('ctrl: ', ctrl[0]);
if (ctrl) {
element.addClass('nested');
} else {
element.addClass('notnested');
}
}
};
});
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;
}
};
});