How siblings are sharing their scope under following directive structure - angularjs

I have following directive structure.
<test-widget-body>
<test-task-list-filter>
</test-task-list-filter>
<test-task-list>
<test-datatable config="dtConfig" columns="dtColumns" api="dtApi"></test-datatable>
</test-task-list>
</test-widget-body>
Here are respective directives:
test-datatable
angular.module('testDatatable').directive('testDatatable', function () {
var directive = {
controller: 'testDatatableController',
restrict: 'E',
replace: true,
scope: {
config: '=',
columns: '=',
api: '=',
},
link: lnkFunction,
template: '<table class="table"></table>',
};
return directive;
}
testTaskList
angular.module('testTask').directive('testTaskList', function () {
return {
transclude: true,
restrict: 'E',
controller: 'testListController',
controllerAs: 'vm',
templateUrl: '/app/test/directives/test-list/test-list.html',
link: {
pre: preLink
}
};
function preLink(){
var dtColumns = [{
----
}];
var dtConfig =[];
var dtApi =[];
}
}
testTaskListFilter
angular.module('testTask').directive('testTaskListFilter', function () {
return {
restrict: 'E',
controller: 'testListFilterController',
templateUrl: '/app/test/directives/test-list/test-list-filter.html'
};
});
testWidgetBody
angular.module('testWidgetGrid').directive('testWidgetBody', function () {
return {
templateUrl: 'test.html',
link: function ($scope, element) {
}
}
Here I'm able to access dtConfig dtColumns dtApi objects inside testListFilterController controller.
How is this possible as <test-task-list-filter> and <test-task-list> are siblings.
Can anyone explain what is happening in this scenario.
Note: I'm able to access that object when I perform some click action not while directive rendering.

If you look at the declarations for testTaskListFilter directive and the testTaskList directive, neither of them have isolate scopes. You can declare isolate scopes by doing one of the following in the directive: scope: true or scope: {} (like in your testDatatable) directive.
Thus, any directive that does not declare its own scope inherits its parent's. So, testTaskListFilter and testTaskList are both using the same scope. This means that you're then able to
access dtConfig dtColumns dtApi objects inside testListFilterController controller
Here's the Angular wiki post for understanding scopes and scope inheritance

Related

How to access function of a parent controller inside the child directive

How can I call a function from parent controller inside the child directive?
my controller looks something like this.
angular.module('myVillage')
.controller('myVillageController', myVillageController);
function myVillageController($scope, $q, $element, $timeout) {
function moveHome() {
console.log("moved home")
}
}
my directive looks something like this.
angular
.module('myVillage')
.directive('myVillagModal', myVillagModal);
var vm = this,
cachedKeys = {},
limitInit = 500;
function myVillagModal(myVillage, $filter) {
return {
restrict: 'E',
templateUrl: myVillagelTemplateUrl,
bindToController: true,
scope: {
items: '=',
selectedItems:'=',
selectedItemsChanged: '&'
},
transclude: true,
controller: myVillageController,
controllerAs: 'vm'
};
function myVillageController() {
//....
}
}
I want to call the moveHOme function inside the move-village directive.
<div ng-controller="myVillageController">
<move-village></move-village>
</div>
You can try
$scope.$parent.moveHome();
from the child controller.
Thank you, people, for trying to answer.
I was missing to pass $scope inside the myVillagecontroller due to that I was not able to access parent controller scope.
here is my answer.
angular
.module('myVillage')
.directive('myVillagModal', myVillagModal);
var vm = this,
cachedKeys = {},
limitInit = 500;
function myVillagModal(myVillage, $filter) {
return {
restrict: 'E',
templateUrl: myVillagelTemplateUrl,
bindToController: true,
scope: {
items: '=',
selectedItems:'=',
selectedItemsChanged: '&'
},
transclude: true,
controller: myVillageController,
controllerAs: 'vm'
};
function myVillageController($scope) {
//$scope.$parent now gives the scope of parent controller
//....
$scope.$parent.moveHome(); //this gives 'moved home'
}
}

Access $scope from within $scope.$on

I have this code:
.directive('hostelGridBed', function($rootScope){
return {
restrict: 'E',
scope: {
config: '=',
range: '=',
days: '='
},
link: function(scope, element){
},
controller: ['$scope', function($scope){
$scope.innerStay = '';
function switchStay(data){
$rootScope.$broadcast('SwitchStay', {data: data.config});
};
$scope.onDropComplete = function(data,evt){
//$rootScope.$broadcast
switchStay(data);
console.log('Drop completo bed!');
}
}],
templateUrl: './js/templates/hostelgridbed.html'
}
})
.directive('hostelGridDay', function(StaysFactory){
return {
restrict: 'E',
scope: {
config: '=',
date: '='
},
link: function(scope,element){
},
controller: ['$scope','$rootScope', function($scope,$rootScope){
$scope.switchStay = function(evt, data){
console.log(data);
// Here. How can I access this controller's $scope
}
$scope.$on('SwitchStay', $scope.switchStay);
}],
templateUrl: './js/templates/hostelgridday.html'
}
})
I get data from $broadcast ok, it's all good. Except that I need to access the directive's $scope from within $scope.switchStay function.
Is there a way to accomplish that?
Actually when you $broadcast or $emit the event from the directive, you could send also the directive's scope as parameter. It should work, but this is actually awful and a really bad practice.
What you have to do in these cases is to add a callback parameter to the directive (&) and provide the switchStay function as callback, and simply trigger it from the directive, passing the parameters that you need to access inside the function.
I hope it makes sense.

Accessing scope from parent directive angularjs

I have two directives and I wanted it to use it like so:
<m-list m-searchable></m-list>
So the two directives were m-list and m-searchable, now I want to access and manipulate the scope of the m-list when I attach the m-searchable directive.
I have this:
'use strict';
angular.module('app')
.directive('mList', function () {
return {
restrict: 'E',
scope: {},
controller: function($rootScope) {
var vm = this;
vm.name = 'joey';
},
controllerAs: 'ctrl',
bindToController: true,
templateUrl: '...'
};
});
And my m-searchable looks like this:
angular.module('app')
.directive('mSearchable', function () {
return {
restrict: 'A',
scope: {},
controllerAs: 'ctrl',
bindToController: true,
replace: true,
controller: function($rootScope, $scope) {
// I want console.log the scope of the directive where I attached the `m-searchable`
}
};
});
I want to console.log the scope of the directive where I attached the m-searchable. How do I access vm.name?
Firstly i think you will encounter $compile error due to both directive try to have their own isolated scope on the same element.
https://docs.angularjs.org/error/$compile/multidir?p0=mList&p1=mSearchable&p2=new%2Fisolated%20scope&p3=%3Cm-list%20m-searchable%3D%22%22%3E
Updated : better approach as suggested at http://juristr.com/blog/2015/01/learning-ng-directives-access-scope-controller/
is to pass the object between controller & directive
<m-list m-searchable name="name"></m-list>
app.directive('mList', function () {
return {
restrict: 'E',
scope: { name: "="},
controller: function($rootScope, $scope) {
var vm = this ;
vm.name = 'joey';
},
controllerAs: 'ctrl',
bindToController: true
};
});
http://plnkr.co/edit/OKVicRzjuH6L0xedF2qB?p=preview

Angular: controller's methods are not working in directive's template

Here is a directive that is loading new Template from file:
.directive('candidatesFilter', function(){
return {
resctict: 'E',
replace: true,
templateUrl: 'views/directives/filters/AAAA.html'
}
})
Next HTML-element calls this directive from the other HTML-Template (e.g. xxx.html):
<candidates-filter></candidates-filter>
There is next controller for this parent Template (xxx.html):
app.controller('candidatesController', function($scope, $location ){
$scope.addPeson = function() {
$location.url('/candidate/0');
};
});
Method addPerson() is not accessible inside the Directive's template AAAA.html, because
data-ng-click="addPerson()"
is not working there. How to change the Directive to make addPerson() method available inside the directive's template?
TEMPORARY Solution
I fixed this issue by next solution
.directive('candidatesFilter', function(){
return {
resctict: 'E',
replace: true,
templateUrl: 'views/directives/filters/AAAA.html',
controller: function(){
$('button.add').on('click',function(){
location.hash = '#/candidate/0';
});
}
}
})
If I understand the problem correctly:
You can pass a function into the directive for it to use
<candidates-filter></candidates-filter>
becomes
<candidates-filter add-candidate="addPerson()"></candidates-filter>
and the directive definition changed as follows:
.directive('candidatesFilter', function() {
return {
resctict: 'E',
replace: true,
scope: {
addCandidate: '&addCandidate'
}
templateUrl: 'views/directives/filters/AAAA.html'
link: function(scope, element, attrs) {
scope.someFunctionInDirective = function() {
scope.addCandidate();
}
};
}
})
Alternatively you can call it with the ng-click like normal from the button
Hope this helps clarify it?

Access Parent Scope in Transcluded Directive

I would like to access a parent directive's scope, but I can't seem to get the right combination of settings. Is this possible and is it the right approach?
I really want to avoid putting something like SOME_CONST (which would help me make DOM updates through control flow) in MyCtrl
<div ng-controller="MyCtrl">
<parent>
<child></child>
</parent>
</div>
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.obj = {prop:'foo'};
}
myApp.directive('parent', function() {
return {
scope: true,
transclude: true,
restrict: 'EA',
template: '<div ng-transclude><h1>I\'m parent {{obj.prop}}<h1></div>',
link: function(scope, elem, attrs) {
scope.SOME_CONST = 'someConst';
}
}
});
myApp.directive('child', function() {
return {
restrict: 'EA',
template: '<h1>I\'m child.... I want to access my parent\'s stuff, but I can\'t. I can access MyCtrlScope though, see <b>{{obj.prop}}</b></h1> how can I access the <b>SOME_CONST</b> value in my parent\'s link function? is this even a good idea? {{SOME_CONST}}. I really don\'t want to put everything inside the MyCtrl',
}
});
Please see this fiddle
Thanks
With transclude: true and scope: true, the parent directive creates two new scopes:
Scope 004 is a result of scope: true, and scope 005 is a result of transclude: true. Since the child directive does not create a new scope, it uses transcluded scope 005. As you can see from the diagram there is no path from scope 005 to scope 004 (except via private property $$prevSibling, which goes in the opposite direction of $$nextSibling -- but don't use those.)
#joakimbl's solution is probably best here, although I think it is more common to define an API on the parent directive's controller, rather than defining properties on this:
controller: function($scope) {
$scope.SOME_CONST = 'someConst';
this.getConst = function() {
return $scope.SOME_CONST;
}
}
Then in the child directive:
link:function(scope,element,attrs,parentCtrl){
scope.SOME_CONST = parentCtrl.getConst();
},
This is how the tabs and pane directives work on Angular's home page ("Create Components" example).
Normally the way you access a parent scope variable in a directive is through bi-directional binding (scope:{model:'=model'} - see the angular guide on directives) in the directive configuration), but since you're using transclusion this is not so straight forward. If the child directive will always be a child of the parent directive you can however configure it to require the parent, and then get access to the parent controller in the child link function:
myApp.directive('parent', function() {
return {
scope: true,
transclude: true,
restrict: 'EA',
template: '<div ng-transclude><h1>I\'m parent {{obj.prop}}<h1></div>',
controller: function($scope) {
$scope.SOME_CONST = 'someConst';
this.SOME_CONST = $scope.SOME_CONST;
}
}
});
myApp.directive('child', function() {
return {
restrict: 'EA',
require:'^parent',
scope:true,
link:function(scope,element,attrs,parentCtrl){
scope.SOME_CONST = parentCtrl.SOME_CONST;
},
template: '<h1>I\'m child.... I want to access my parent\'s stuff, but I can\'t. I can access MyCtrlScope though, see <b>{{obj.prop}}</b></h1> how can I access the <b>SOME_CONST</b> value in my parent\'s link function? is this even a good idea? {{SOME_CONST}}. I really don\'t want to put everything inside the MyCtrl',
}
});
See this update: http://jsfiddle.net/uN2uv/
I just had the same problem and finally solved it with the angular manual ;)
In short: you need to use a controller in your parent directive and require that controller in your child directive. This way you are able to get your parent properties.
See https://docs.angularjs.org/guide/directive
Chapter: Creating Directives that Communicate
I changed your fiddle to use a controller, now you can access your constant:
https://jsfiddle.net/bbrqdmt3/1/
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.obj = {prop:'foo'};
}
myApp.directive('parent', function() {
return {
scope: true,
transclude: true,
restrict: 'EA',
template: '<div ng-transclude><h1>I\'m parent {{obj.prop}}<h1></div>',
controller: function($scope) {
this.getConst= function() {
return 'someConst';
}
},
}
});
myApp.directive('child', function() {
return {
restrict: 'EA',
require : '^parent',
link: function(scope, element, attrs, ctrl) {
scope.value= ctrl.getConst();
},
template: '<h1>I\'m child.... I want to access my parent\'s stuff, but I can\'t. I can access MyCtrlScope though, see <b>{{obj.prop}}</b></h1> how can I access the <b>SOME_CONST</b> value in my parent\'s link function? is this even a good idea? {{value}}. I really don\'t want to put everything inside the MyCtrl',
}
});
There's a transclude fn in the arguments of the link fn after the controller.
myApp.directive('parent', function() {
return {
scope: true,
transclude: true,
restrict: 'EA',
template: '<div><h1>I'm a parent header.</h1></div>',
link: function (scope, el, attrs, ctrl, transclude) {
transclude(scope, function (clone, scope) {
element.append(clone); // <-- will transclude it's own scope
});
},
controller: function($scope) {
$scope.parent = {
binding: 'I\'m a parent binding'
};
}
}
});
myApp.directive('child', function() {
return {
restrict: 'EA',
require:'^parent',
scope:true,
link:function(scope,element,attrs,parentCtrl){
},
template: '<div>{{parent.binding}}</div>' // <-- has access to parent's scope
}
});

Resources