How can I add fade animation to a tabset using angular-ui-bootstrap?
For example, given the following code:
<tabset>
<tab heading="Tab1">Some content</tab>
<tab heading="Tab2">Other content</tab>
</tabset>
I would like the content of the tabs to fade when switching between them. I tried to add thefade
class to the tab
tags (similar to how you would do it with the bootstrap3 js file), but it didn't work.
Many thanks!
Since tabset use ng-class
to control "active" tab, which allow us to define fade effect with angular animation by setting opacity=0 when "active" class is removed/attached.
First, you need to load ngAnimate
module by including angular-animate.js
and set up dependency.
Add to your <head>
:
<script src="https://code.angularjs.org/1.2.24/angular-animate.js"></script>
Set module dependency:
angular.module("myApp", ["ui.bootstrap", "ngAnimate"]);
Now add animation class to your tabset.
<tabset class="tab-animation">
<tab heading="Tab1">Some content</tab>
<tab heading="Tab2">Other content</tab>
</tabset>
Put following code into your css file:
/* set reference point */
.tab-animation > .tab-content {
position: relative;
}
/* set animate effect */
.tab-animation > .tab-content > .tab-pane{
transition: 0.2s linear opacity;
}
/* overwrite display: none and remove from document flow */
.tab-animation > .tab-content > .tab-pane.active-remove {
position: absolute;
top: 0;
width: 100%;
display: block;
}
/* opacity=0 when removing "active" class */
.tab-animation > .tab-content > .tab-pane.active-remove-active {
opacity: 0;
}
/* opacity=0 when adding "active" class */
.tab-animation > .tab-content > .tab-pane.active-add {
opacity: 0;
}
That's all. You can check the demo on Plunker.
Also take a look at ngAnimate doc.
I ended up patching the ui-bootstrap file.
I'm still a noob with AngularJS, so please forgive the lingo.
This is an unconventional hack, and needs to be refactored with ng-animate, but it works.
Open ui-bootstrap-tpls-0.10.0.js and look for the 'tab' directive :
.directive('tab', ['$parse', function($parse) {
return {
require: '^tabset',
restrict: 'EA',
replace: true,
templateUrl: 'template/tabs/tab.html',
transclude: true,
scope: {
id:'@', // PATCH : GETTING TAB 'id' ATTRIBUTE
heading: '@',
onSelect: '&select', //This callback is called in contentHeadingTransclude
//once it inserts the tab's content into the dom
onDeselect: '&deselect'
},
// ...
Notice the extra code for retrieving the id attribute value (via transclusion, I guess).
A few lines below, look for :
scope.$watch('active', function(active) {
and patch it like so :
scope.$watch('active', function(active) {
// Note this watcher also initializes and assigns scope.active to the
// attrs.active expression.
setActive(scope.$parent, active);
if (active) {
tabsetCtrl.select(scope);
scope.onSelect();
tab_id = attrs.id;
$(".tab_pane_"+tab_id).hide(); // HIDE AT FIRST, SO IT CAN ACTUALLY FADE IN
$(".tab_pane_"+tab_id).fadeIn(1000); // JQUERY TARGETING BY CLASS
} else {
scope.onDeselect();
tab_id = attrs.id;
$(".tab_pane_"+tab_id).hide(); // JQUERY TARGETING BY CLASS
}
});
A few lines below, look for :
scope.select = function() {
and add inside :
$(".tab-pane").hide();
so all tab panes hide properly at first.
Then, look for :
angular.module("template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) { ...
and add the css class to the tab-pane element in the corresponding template, like so :
angular.module("template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) {
$templateCache.put("template/tabs/tabset.html",
"\n" +
"<div class=\"tabbable\">\n" +
" <ul class=\"nav {{type && 'nav-' + type}}\" ng-class=\"{'nav-stacked': vertical, 'nav-justified': justified}\" ng-transclude></ul>\n" +
" <div class=\"tab-content\">\n" +
" <div class=\"tab-pane tab_pane_{{tab.id}}\" \n" + // CLASS NAME IS DYNAMIC
" ng-repeat=\"tab in tabs\" \n" +
" ng-class=\"{active: tab.active}\"\n" +
" tab-content-transclude=\"tab\">\n" +
" </div>\n" +
" </div>\n" +
"</div>\n" +
"");
}]);
Once the ui-bootstrap .js file is modified, you must edit your view template (where you fetch the tabs) and declare the 'id' attribute :
<!-- TABS -->
<tabset justified="true">
<tab ng-repeat="tab in tabs" heading="{{tab.title}}" id="{{tab.id}}" >
// ... TAB CONTENT
You should get the basic concept, currently it's not very elegant (to put it mildly).
But it works.
In case you wonder how my tabs got id's, well, I injected them in my controller :
Tab1 = {
id:1,
'ShortDescription': ShortDescription,
'FullDescription': FullDescription,
'TabContent': TabContent1,
title: "ProductTabTitleDefault1",
// active:true
};
Tab2 = {
id:2,
'ShortDescription': ShortDescription,
'FullDescription': FullDescription,
'TabContent': TabContent1,
title: "ProductTabTitleDefault2",
// active:true
};
$rootScope.tabs = {
'Tab1': Tab1,
'Tab2': Tab2,
};
Of course this is mockup data, but assuming your tabs and their content are dynamic, you can use a counter, and maybe use another key instead of "id" (but you'll have to change the rest accordingly).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With