Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fade effect for tabs in ui-bootstrap (Angular.JS)

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!

like image 938
urish Avatar asked Oct 21 '13 19:10

urish


2 Answers

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.

like image 190
eight04 Avatar answered Sep 22 '22 18:09

eight04


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).

like image 34
Christian Bonato Avatar answered Sep 21 '22 18:09

Christian Bonato