Skip to content
This repository has been archived by the owner on May 29, 2019. It is now read-only.

Commit

Permalink
fix(tabs): fix tab content compiling wrong (Closes #599, #631, #574)
Browse files Browse the repository at this point in the history
* Before, tab content was being transcluded before the tab content area
 was ready. This forced us to disconnect the tab contents from the DOM
 temporarily, then reocnnect them later. This caused a lot of problems.
* Now, neither the tab content or header are transcluded until both
 the heading and content areas are loaded. This is simpler and fixes
 many weird compilation bugs.
  • Loading branch information
ajoslin committed Jul 8, 2013
1 parent f5b37f6 commit 224bc2f
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 47 deletions.
78 changes: 31 additions & 47 deletions src/tabs/tabs.js
Expand Up @@ -15,20 +15,16 @@ angular.module('ui.bootstrap.tabs', [])
};
})

.controller('TabsetController', ['$scope', '$element',
.controller('TabsetController', ['$scope', '$element',
function TabsetCtrl($scope, $element) {

//Expose the outer scope for tab content compiling, so it can compile
//on outer scope like it should
this.$outerScope = $scope.$parent;

var ctrl = this,
tabs = ctrl.tabs = $scope.tabs = [];

ctrl.select = function(tab) {
angular.forEach(tabs, function(tab) {
tab.active = false;
});
});
tab.active = true;
};

Expand All @@ -39,7 +35,7 @@ function TabsetCtrl($scope, $element) {
}
};

ctrl.removeTab = function removeTab(tab) {
ctrl.removeTab = function removeTab(tab) {
var index = tabs.indexOf(tab);
//Select a new tab if the tab to be removed is selected
if (tab.active && tabs.length > 1) {
Expand Down Expand Up @@ -101,7 +97,7 @@ function TabsetCtrl($scope, $element) {
* @param {boolean=} active A binding, telling whether or not this tab is selected.
* @param {boolean=} disabled A binding, telling whether or not this tab is disabled.
*
* @description
* @description
* Creates a tab with a heading and content. Must be placed within a {@link ui.bootstrap.tabs.directive:tabset tabset}.
*
* @example
Expand Down Expand Up @@ -235,37 +231,11 @@ function($parse, $http, $templateCache, $compile) {
//value won't overwrite what is initially set by the tabset
if (scope.active) {
setActive(scope.$parent, true);
}
}

//Transclude the collection of sibling elements. Use forEach to find
//the heading if it exists. We don't use a directive for tab-heading
//because it is problematic. Discussion @ http://git.io/MSNPwQ
transclude(scope.$parent, function(clone) {
//Look at every element in the clone collection. If it's tab-heading,
//mark it as that. If it's not tab-heading, mark it as tab contents
var contents = [], heading;
angular.forEach(clone, function(el) {
//See if it's a tab-heading attr or element directive
//First make sure it's a normal element, one that has a tagName
if (el.tagName &&
(el.hasAttribute("tab-heading") ||
el.hasAttribute("data-tab-heading") ||
el.tagName.toLowerCase() == "tab-heading" ||
el.tagName.toLowerCase() == "data-tab-heading"
)) {
heading = el;
} else {
contents.push(el);
}
});
//Share what we found on the scope, so our tabHeadingTransclude and
//tabContentTransclude directives can find out what the heading and
//contents are.
if (heading) {
scope.headingElement = angular.element(heading);
}
scope.contentElement = angular.element(contents);
});
//We need to transclude later, once the content container is ready.
//when this link happens, we're inside a tab heading.
scope.$transcludeFn = transclude;
};
}
};
Expand All @@ -274,7 +244,7 @@ function($parse, $http, $templateCache, $compile) {
.directive('tabHeadingTransclude', [function() {
return {
restrict: 'A',
require: '^tab',
require: '^tab',
link: function(scope, elm, attrs, tabCtrl) {
scope.$watch('headingElement', function updateHeadingElement(heading) {
if (heading) {
Expand All @@ -290,17 +260,31 @@ function($parse, $http, $templateCache, $compile) {
return {
restrict: 'A',
require: '^tabset',
link: function(scope, elm, attrs, tabsetCtrl) {
var outerScope = tabsetCtrl.$outerScope;
scope.$watch($parse(attrs.tabContentTransclude), function(tab) {
elm.html('');
if (tab) {
elm.append(tab.contentElement);
$compile(tab.contentElement)(outerScope);
}
link: function(scope, elm, attrs) {
var tab = scope.$eval(attrs.tabContentTransclude);

//Now our tab is ready to be transcluded: both the tab heading area
//and the tab content area are loaded. Transclude 'em both.
tab.$transcludeFn(tab.$parent, function(contents) {
angular.forEach(contents, function(node) {
if (isTabHeading(node)) {
//Let tabHeadingTransclude know.
tab.headingElement = node;
} else {
elm.append(node);
}
});
});
}
};
function isTabHeading(node) {
return node.tagName && (
node.hasAttribute('tab-heading') ||
node.hasAttribute('data-tab-heading') ||
node.tagName.toLowerCase() === 'tab-heading' ||
node.tagName.toLowerCase() === 'data-tab-heading'
);
}
}])

;
Expand Down
39 changes: 39 additions & 0 deletions src/tabs/test/tabsSpec.js
Expand Up @@ -495,4 +495,43 @@ describe('tabs', function() {
expect(tabChild.inheritedData('$tabsetController')).toBeTruthy();
});
});

//https://github.com/angular-ui/bootstrap/issues/631
describe('ng-options in content', function() {
var elm;
it('should render correct amount of options', inject(function($compile, $rootScope) {
var scope = $rootScope.$new();
elm = $compile('<tabset><tab><select ng-model="foo" ng-options="i for i in [1,2,3]"></tab>')(scope);
scope.$apply();

var select = elm.find('select');
scope.$apply();
expect(select.children().length).toBe(4);
}));
});

//https://github.com/angular-ui/bootstrap/issues/599
describe('ng-repeat in content', function() {
var elm;
it('should render ng-repeat', inject(function($compile, $rootScope) {
var scope = $rootScope.$new();
scope.tabs = [
{title:'a', array:[1,2,3]},
{title:'b', array:[2,3,4]},
{title:'c', array:[3,4,5]}
];
elm = $compile('<div><tabset>' +
'<tab ng-repeat="tab in tabs" heading="{{tab.title}}">' +
'<tab-heading>{{$index}}</tab-heading>' +
'<span ng-repeat="a in tab.array">{{a}},</span>' +
'</tab>' +
'</tabset></div>')(scope);
scope.$apply();

var contents = elm.find('.tab-pane');
expect(contents.eq(0).text().trim()).toEqual('1,2,3,');
expect(contents.eq(1).text().trim()).toEqual('2,3,4,');
expect(contents.eq(2).text().trim()).toEqual('3,4,5,');
}));
});
});

0 comments on commit 224bc2f

Please sign in to comment.