Creating a Stylish Tab Fade Animation with ui-bootstrap in AngularJS

Is there a way to incorporate a fade animation into a tabset using angular-ui-bootstrap?

Consider the following code:

<tabset>
    <tab heading="Tab1">Some content</tab>
    <tab heading="Tab2">Other content</tab>
</tabset>

I am looking for a solution where the content of each tab fades in and out when switching between them. I attempted to apply the fade class to the tab elements (similar to how it is done with the bootstrap3 js file), but unfortunately, this did not produce the desired effect.

Any suggestions or guidance would be greatly appreciated!

Answer №1

By utilizing the ng-class attribute in tabsets, we can implement a fade effect using Angular animations by adjusting the opacity property when the "active" class is added or removed.

To start, make sure to load the ngAnimate module by including angular-animate.js and declaring it as a dependency.

Include this in your <head>:

<script src="https://code.angularjs.org/1.2.24/angular-animate.js"></script>

Add ngAnimate to your module's dependencies:

angular.module("myApp", ["ui.bootstrap", "ngAnimate"]);

Next, apply the animation class to your tabset.

<tabset class="tab-animation">
    <tab heading="Tab1">Some content</tab>
    <tab heading="Tab2">Other content</tab>
</tabset>

Insert the following code into your CSS file:

/* set reference point */
.tab-animation > .tab-content {
    position: relative;
}

/* define animation effect */
.tab-animation > .tab-content > .tab-pane{
    transition: 0.2s linear opacity;
}

/* adjust display for animation */
.tab-animation > .tab-content > .tab-pane.active-remove {
    position: absolute;
    top: 0;
    width: 100%;
    display: block;
}

/* decrease opacity on removing "active" class */
.tab-animation > .tab-content > .tab-pane.active-remove-active {
    opacity: 0;
}

/* decrease opacity on adding "active" class */
.tab-animation > .tab-content > .tab-pane.active-add {
    opacity: 0;
}

That's it. You can view the demo on Plunker for a visual representation.

For more information, refer to the ngAnimate documentation.

Answer №2

After making some adjustments to the ui-bootstrap file, I was able to solve the issue. Since I'm still relatively new to AngularJS, please excuse any unfamiliar terminology. Although it's not a standard approach, this makeshift solution does get the job done.

Navigate to ui-bootstrap-tpls-0.10.0.js and locate 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'
    },
    // ...

Take note of the added code for fetching the id attribute value (presumably through transclusion).



A few lines down, find :

     scope.$watch('active', function(active) {

and modify it as follows :

          scope.$watch('active', function(active) {
      // 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
      }

    });



Scroll a bit further down to find :

    scope.select = function() {

Insert the following line inside the function :

    $(".tab-pane").hide();

This ensures all tab panes are properly hidden initially.



Next, look for :

angular.module("template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) { ...

Add a 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 you've modified the ui-bootstrap .js file, remember to update your view template (where you load the tabs) and specify the 'id' attribute :

    <!-- TABS -->
    <tabset justified="true">
        <tab ng-repeat="tab in tabs" heading="{{tab.title}}" id="{{tab.id}}" >
            // ... TAB CONTENT



The concept might seem crude at the moment, but it functions effectively.


If you're curious about how my tabs obtained IDs, I injected them through 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, 
                        };

Remember, this data is fictitious, but if your tabs and their content are dynamic, you can utilize a counter and consider using a different key instead of "id" (adjusting accordingly).

Answer №3

I wanted to share a different approach to achieve the desired effect instead of using the solution provided by @user3413125. My method involves utilizing @keyframes for a cross-fade effect, as opposed to a fade out and then a fade in effect. You can view a demonstration of this on Plunker

Below is the CSS code for the fade-in animation (the fade-out animation follows a similar structure):

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

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

This keyframe technique was inspired by the AngularJs tutorial 14 - specifically the section titled "CSS Keyframe Animations: Animating ngView", located halfway through the page.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What are some techniques for managing scrolling within a particular element?

I am currently working with Vue.js and utilizing Element UI components. I want to incorporate a scroll management function to achieve infinite scrolling. To better understand, please refer to the screenshot in the Example section: Despite trying differen ...

Tips for substituting a pair of terms with equivalent ones from an array using Arabic JavaScript

In my array, I have the months written in English and the days of the week also in English. I am trying to replace the English words with their Arabic equivalents at the corresponding index in the array. My code looks simple and clean, but for some reason ...

Make sure to wait until the fetch function is finished before triggering another action

When I run console.log(this.detaliiMP), it currently returns an empty array. My goal is to wait for the getData() function to retrieve the data and populate the detaliiMP array before logging it to the console. Check out the demo here const app = Vue.c ...

Uploading a three.js canvas to the server without saving it as a file

Currently, I am in the process of saving an image to the server using a three.js script that I have created. The script looks like the following: actualCode(THREE); function actualCode(THREE) { //Rendering variables const renderer = new THREE.WebG ...

Implementing event handling with .On() in Jquery following .Off()

I need assistance with my responsive navigation bar. I am having trouble with the jQuery code to disable hover events if the width is less than or equal to 768px and enable it for desktop screens. $(window).on('load resize', function (e) { v ...

Why won't the JavaScript work when linking to a carousel slide from another page?

Trying to follow this 6-year-old guide, but my JavaScript isn't triggering when the URL contains #slide_ - have things changed? Linking to a specific Bootstrap carousel slide from another page My code on page 2: <!doctype html> <html> &l ...

Setting CSS attributes in React.js using a method similar to setState

What is the method to specify CSS in React.js? Here’s the scenario: I have a bar that starts at full height and then reduces in height through animation until it reaches 0px. Refer to the image below. https://i.sstatic.net/6cJFk.png The process works ...

Change element position to relative while scrolling

I created a wrapper with an animation similar to the one on Apple's Airpods Pro page. It features a video that plays gradually as I scroll, with the text smoothly scrolling over it within a specific area (text-display). So far, this part is working w ...

Issue with $routeChangeStart and other similar events not being triggered during page initialization

Within my angular web application, $routeProvider .when('/topic/:keyword/heath-feed', { controller: 'hfController', }) .when('/topic/:keyword/heath-feed/:storyType', { controller: 'hfControll ...

Using Angular with a hapi.js server that supports JSONP data requests

In my project, there is an endpoint specifically set at /api/profile that accepts post parameters. var http = require('http'); var serverConfig = require('../server.config.js'); var request = require('request'); module.expo ...

Issue with ng-file-upload and Busboy causing upload error on server

I am facing a situation where I need to upload both a .zip file and a .xlsx file to an endpoint simultaneously. On the client side (Angular 1): files.upload = Upload.upload({ url: '/api/files', data: { xlsxFile: xlsxFile, zipFile: zipFile } ...

What is the process for sorting an item based on a specific criteria?

I am working with an object that looks like this: [insert image description here][1] The object on the screen is represented by dataUserProfile.permissions[dataOriginSelect].permissions I am trying to sort this object based on the 'order' para ...

Leverage the potential of the value parameter within a mongoose scope

I am currently trying to retrieve emails of a user based on their id. However, I have encountered an issue due to the asynchronous nature of the mongoose function. Mail.find({receiver_id: '#id#'}, function(err, mails) { var result = []; ...

Error: Unable to access the 'center' property of an undefined value in the three.module.js file

I started delving into coding a few months back, with my focus on mastering three.js/react. Currently, I am engrossed in a tutorial located at [https://redstapler.co/three-js-realistic-rain-tutorial/], where I aim to create a lifelike rain background. The ...

Explore Dreamfactory's User Management features for apps with varying user roles and permissions

I am currently working on an application that requires user management with various roles to access different data views stored in a MS SQL Server database. To streamline the process, I am using dreamfactory to create a REST API for this data. My goal is t ...

Combining arrays to append to an array already in place

I have implemented the rss2json service to fetch an rss feed without pagination support. Instead of a page parameter, I can utilize the count parameter in my request. With this setup, I am successfully able to retrieve and display the feed using a service ...

Is there a way to retrieve the value of bindings in the component controller using Angular 1.5 and Typescript?

In my quest to develop a versatile left-hand menu component that can dynamically display different menu structures based on input strings, I stumbled upon an interesting challenge. By binding a string to the <left-hand-menu-component> element like so ...

AngularJS is notorious for its heavy usage of browser memory. I am curious about how the garbage collection process operates when dealing with

My AngularJS project involves presenting a large dataset, which initially loads in a few seconds. Although the instant search feature works a bit sluggishly, it eventually causes Chrome to complain about excessive memory usage and prompt for continuation. ...

Tips on preventing repeated data fetching logic in Next.js App Routes

I'm currently developing a project with Next.js 13's latest App Routes feature and I'm trying to figure out how to prevent repeating data fetching logic in my metadata generation function and the actual page component. /[slug]/page.tsx expo ...

tips for recognizing the location of an item in an array

When loading an array of objects into a select box, how can I identify which object the user has selected so that I can use it for further work? For instance, if the user chooses the 2nd object in the array, how do I know that their selection corresponds ...