Parent directive controller undefined when passing to child directive - angularjs

I asked general question here in this post. I've got answer with working example; however when I try to use this example to modify existing code, I get error.
See my code below and in this Plunker page.
Html
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.3/angular.min.js"></script>
<div ng-app="myApp">
<tmp-menu ng-disabled="true">
<tmp-menu-link></tmp-menu-link>
<tmp-menu-link></tmp-menu-link>
</tmp-menu>
</div>
JavaScript(AngularJS):
angular.module('myApp', [])
.controller('MyDirectiveController', MyDirectiveController)
.directive('tmpMenu', function() {
return {
restrict: 'AE',
replace:true,
transclude:true,
scope:{
disabled: '=?ngDisabled'
},
controller: 'MyDirectiveController',
template: '<div>myDirective Disabled: {{ disabled }}<ng-transclude></ng-transclude></div>',
link: function(scope, element, attrs) {
}
};
})
.directive('tmpMenuLink', function() {
return {
restrict: 'AE',
replace:true,
transclude:true,
scope:{
},
required:'^^tmpMenu',
template: '<div>childDirective disabled: {{ disabled }}</div>',
link: function(scope, element, attrs, MyDirectiveCtrl) {
console.log(MyDirectiveCtrl);
scope.disabled = MyDirectiveCtrl.isDisabled();
}
};
})
function MyDirectiveController($scope) {
this.isDisabled = function() {
return $scope.disabled;
};
}
Inside directive tmpMenuLink, MyDirectiveCtrl is undefined.
Why is that?

You have a typo in your code:
required:'^^tmpMenu',
change it to
require:'^^tmpMenu',
Check this plunkr
https://plnkr.co/edit/DgyW3OFgr1GyAR8fuATi?p=preview

Because it's require not required.
angular.module('myApp', [])
.controller('MyDirectiveController', MyDirectiveController)
.directive('tmpMenu', function() {
return {
restrict: 'AE',
replace: true,
transclude: true,
scope: {
disabled: '=?ngDisabled'
},
controller: 'MyDirectiveController',
template: '<div>myDirective Disabled: {{ disabled }}<ng-transclude></ng-transclude></div>',
link: function(scope, element, attrs) {}
};
})
.directive('tmpMenuLink', function() {
return {
restrict: 'AE',
replace: true,
transclude: true,
require: '^^tmpMenu',
template: '<div>childDirective disabled: {{ disabled }}</div>',
link: function(scope, element, attrs, MyDirectiveController) {
scope.disabled = MyDirectiveController.isDisabled();
}
};
})
function MyDirectiveController($scope) {
this.isDisabled = function() {
return $scope.disabled;
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.3/angular.min.js"></script>
<div ng-app="myApp">
<tmp-menu ng-disabled="true">
<tmp-menu-link></tmp-menu-link>
<tmp-menu-link></tmp-menu-link>
</tmp-menu>
</div>

Related

AngularJS values

How do I get the FirstName value in my directive?
<hello-World value="{{item.FirstName}}"> </hello-World>
app.directive('helloWorld', function() {
return {
restrict: 'AE',
replace: 'true',
template: '<h3>Hello ??????</h3>'
};
});
To get values you need to use scope parameter in directive. Read more from directives here https://docs.angularjs.org/guide/directive
Check this example
angular.module('myApp', [])
.directive('helloWorld', function() {
return {
restrict: 'AE',
replace: 'true',
template: '<h3>Hello {{value}}</h3>',
scope: {
value: '#'
},
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<hello-World value='World' />
</div>
app.directive('helloWorld', function() {
return {
restrict: 'AE',
replace: 'true',
template: '<h3>Hello {{value}}</h3>',
scope: {
value: '='
}
};
});

Function inside directive not being triggered

Why my function toggleCategory function is not being triggered?
Directive
app.directive('asideFilter', function() {
return {
restrict: 'E',
scope: {
toggleCategory: '=&onToggleCategory'
},
transclude: true,
templateUrl: 'assets/directives/asideFilter/asideFilter.html',
link: function(scope, element, attrs){
}
};
});;
Directive template
<div ng-click="toggleCategory({id: 'teste'})">TEST</div>
Parent scope
$scope.onToggleCategory = function () {
console.log('onToggleCategory');
}
Parent Caller
<aside-filter toggle-category="onToggleCategory({id: 'test '})" categories="categories" />
You can call the parent controller function like this also
app.directive('asideFilter', function() {
return {
restrict: 'E',
scope: {
toggleCategory: '&',
},
transclude: true,
templateUrl: 'assets/directives/asideFilter/asideFilter.html',
link: function(scope, element, attrs){
scope.toggleCategoryClick = function (value) {
scope.toggleCategory(value);
}
}
};
});
Directive template
<div ng-click="toggleCategoryClick({id: 'teste'})">TEST</div>
Directive Element
<aside-filter toggle-category="onToggleCategory({id: 'test '})" categories="categories" />

AngularJS - accessing parent directive properties from child directives

This should not be too hard a thing to do but I cannot figure out how best to do it.
I have a parent directive, like so:
directive('editableFieldset', function () {
return {
restrict: 'E',
scope: {
model: '='
},
replace: true,
transclude: true,
template: '
<div class="editable-fieldset" ng-click="edit()">
<div ng-transclude></div>
...
</div>',
controller: ['$scope', function ($scope) {
$scope.edit = ->
$scope.editing = true
// ...
]
};
});
And a child directive:
.directive('editableString', function () {
return {
restrict: 'E',
replace: true,
template: function (element, attrs) {
'<div>
<label>' + attrs.label + '</label>
<p>{{ model.' + attrs.field + ' }}</p>
...
</div>'
},
require: '^editableFieldset'
};
});
How can I easily access the model and editing properties of the parent directive from the child directive? In my link function I have access to the parent scope - should I use $watch to watch these properties?
Put together, what I'd like to have is:
<editable-fieldset model="myModel">
<editable-string label="Some Property" field="property"></editable-string>
<editable-string label="Some Property" field="property"></editable-string>
</editable-fieldset>
The idea is to have a set of fields displayed by default. If clicked on, they become inputs and can be edited.
Taking inspiration from this SO post, I've got a working solution here in this plunker.
I had to change quite a bit. I opted to have an isolated scope on the editableString as well because it was easier to bind in the correct values to the template. Otherwise, you are going to have to use compile or another method (like $transclude service).
Here is the result:
JS:
var myApp = angular.module('myApp', []);
myApp.controller('Ctrl', function($scope) {
$scope.myModel = { property1: 'hello1', property2: 'hello2' }
});
myApp.directive('editableFieldset', function () {
return {
restrict: 'E',
scope: {
model: '='
},
transclude: true,
replace: true,
template: '<div class="editable-fieldset" ng-click="edit()"><div ng-transclude></div></div>',
link: function(scope, element) {
scope.edit = function() {
scope.editing = true;
}
},
controller: ['$scope', function($scope) {
this.getModel = function() {
return $scope.model;
}
}]
};
});
myApp.directive('editableString', function () {
return {
restrict: 'E',
replace: true,
scope: {
label: '#',
field: '#'
},
template: '<div><label>{{ label }}</label><p>{{ model[field] }}</p></div>',
require: '^editableFieldset',
link: function(scope, element, attrs, ctrl) {
scope.model = ctrl.getModel();
}
};
});
HTML:
<body ng-controller="Ctrl">
<h1>Hello Plunker!</h1>
<editable-fieldset model="myModel">
<editable-string label="Some Property1:" field="property1"></editable-string>
<editable-string label="Some Property2:" field="property2"></editable-string>
</editable-fieldset>
</body>
You can get access to parent controller by passing attribute in child directive link function
link: function (scope, element, attrs, parentCtrl) {
parentCtrl.$scope.editing = true;
}

AngularJS - Share ngModel with nested directives and transcluded directives

I am trying to create 3 directives:
.directive('dirOne', function () {
return {
restrict: 'E',
transclude: true,
replace: true,
controller: function ($scope, $element, $attrs) {
this.add = function (tag) {
tag && $scope.tags.push(tag);
};
},
template: '<div><p>Bucket from directive: {{tags}}</p><div ng-transclude></div></div>'
};
})
.directive('dirTwo', function () {
return {
restrict: 'A',
replace: true,
require: '^dirOne',
link: function (scope, element, attrs, dirOne) {
scope.add = function (tag) {
dirOne.add(tag);
};
},
template: '<div>'+
' <input type="text" ng-model="query" datum="sugestions" dir-three>' +
' <button ng-click="add(query)">add</button>' +
'</div>'
};
})
.directive('dirThree', ['$compile', function ($compile) {
var itemsTemplate = '<span class="sugestions"><span ng-repeat="item in datum|filter:query">{{item.name||item}}</span></span>';
return {
restrict: 'A',
transclude: true,
replace: true,
require: 'ngModel',
scope: {
datum: '=',
query: '=ngModel'
},
link: function (scope, element, attrs) {
var parent;
element.wrap('<span>');
parent = element.parent();
parent.append(angular.element($compile(itemsTemplate)(scope)));
}
};
}])
In the dirTwo and dirThree, I have an input <input type="text" ng-model="query" datum="sugestions" dir-three> with ngModel, this input needs to access and modify the content of ngModel, so that the scope is not isolated.
http://jsfiddle.net/joaoneto/hbABU/3/
Update
I Updated version, fix some mistakes I had committed, the content that was being transcluded in dirTwo, should not have the "ADD" function, and belongs to dirTree, hope it helps someone and apologize for peopl take to update this entry... see in http://jsfiddle.net/hbABU/4/

Accessing parent's parent controllers through Angular require attribute

In the below example, the accordion-group directive shares a controller and scope with the parent accordion, using the "require: '^accordion'" attribute within the accordion-group directive.
If I wanted to create a child directive, under accordion-group, how would it access the accordion controller as well? Requiring ^accordion and ^accordionGroup do not seem to work.
https://github.com/angular-ui/bootstrap/blob/master/src/accordion/accordion.js
It does work that way. I was just being stupid.
Fiddle for posterity here.
'use strict';
angular.module('myApp', []).controller('OneController', function() {
this.test = function(element) {
element.css('color', 'red');
}
}).directive('one', function() {
return {
restrict: 'E',
transclude: true,
replace: true,
controller: 'OneController',
template: '<span ng-transclude>And a </span>',
}
}).directive('two', function() {
return {
restrict: 'E',
transclude: true,
replace: true,
require: '^one',
template: '<span ng-transclude>and a </span>',
}
}).directive('three', function() {
return {
restrict: 'E',
transclude: true,
replace: true,
require: '^one',
template: '<span ng-transclude>and a one two</span>',
link: function(scope, element, attrs, ctrl) {
ctrl.test(element);
}
}
});

Resources