Reputation: 973
I've created a tab control using angular directives. It is consist of tab, tab-item directives with new scope and tab-item-header, tab-item-body directives for which scope is not declared.
If I understand correctly these directives use scope of tab-item directive because they are placed inside it. But when I try to get in markup property index which is declared inside tab-item scope it is undefined.
app.directive('tabItemHeader', function(){
return {
require: '^tabItem',
transclude: true,
template: '<div ng-click="$parent.setCurrentTab(index)" ng-transclude></div>',
};});
app.directive('tabItemBody', function(){
return {
require: '^tabItem',
transclude: true,
template: '<div ng-show="index==$parent.currentTabIndex"><div ng-transclude></div></div>'
};});
I've created a plunk http://plnkr.co/edit/HkXIOt8FKMw4Ja2GZtF1?p=preview to demonstrate it.
What is wrong?
Upvotes: 2
Views: 2555
Reputation: 1156
http://www.undefinednull.com/2014/02/11/mastering-the-scope-of-a-directive-in-angularjs/
This site explains scope inheritance very well.
Basically, if you add a scope section to the directive that you want to have you have a few options:
scope: false # this inherits the parent scope and allows upstream and downstream traffic (parent <=> child)
scope: true # this inherits the parent scope and allows downstream traffic (parent => child)
scope: {} # this does not inherit and creates the directive's very own scope (you can still inherit by specifying what you want to come down)
More details on specifying scope to inherit via the last option: What is the difference between & vs @ and = in angularJS
Upvotes: 3
Reputation: 40318
(EDIT) Giving some thought after the conversation in the comments, I came up with a better solution. Here is the modified plunk:
http://plnkr.co/edit/djNk8PPzXvngZOvAirMu?p=preview
The key points of this implementation are:
Every directive transcludes its content. This means that, even the innermost directives have access to the outer scope, as is expected. So no more $parent.$parent...
awfulness.
Every directive has an isolated scope. As per the docs the isolated scope is side-by-side with the transcluded one; therefore all the private state of the directives (in this case the active tab, the index of each tabItem
and some directive-specific functions) is kept in the isolated scope.
The directives communicate through the controllers. This pattern requires a top level "coordinator" (here the tab
for all descendant directives and the tabItem
for the tabItemHeader
and tabItemBody
).
By the way, if you want tabs, I would suggest Angular UI.
This was a crazy puzzler.
The reason for your problem is that the tabItem
directive had no reason to transclude its contents; this transclusion created a sibling scope that totally messed up your logic!
Thus the answer is simple: remove these lines from the tabItem
directive:
// REMOVE THEM!!!
transclude: true,
template: '<div ng-transclude></div>',
A plunkr with these lines commented out that prints scope ids: http://plnkr.co/edit/bBfEej145s1YjcE9n3Lj?p=preview (this helps with debugging; see what happens when you include these lines, the templates and the linker function see different scopes!)
And your plunkr forked, with those lines commented out: http://plnkr.co/edit/tgZqjZk0bRCyZiHKM0Sy?p=preview
Upvotes: 5