简体   繁体   中英

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

I have an alternative solution to @user3413125's well-written solution above. It uses @keyframes to achieve a cross-fade, rather than a fade out followed by a fade in. See demo on Plunker

Here is the fade-in portion of the CSS (fade-out is similar):

.tab-animation > .tab-content > .tab-pane.active-add {
    animation: 1s fade-in;
}

@keyframes fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

The keyframe technique is taken from the AngularJs tutorial 14 - look for "CSS Keyframe Animations: Animating ngView", about halfway down the page.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM