Exploring a different method for implementing animations during UI-router state transitions

My product owner presented me with a seemingly impossible challenge to create animations between states. I utilized ngAnimate and thought I had a brilliant solution - only to be told it wasn't what they wanted.

"This isn't what I had in mind," he said.

To provide more clarity, as I switch between panels of a collapse widget, I aim to alter the state and update the URL simultaneously.

How can I incorporate animation into state transitions using an accordion-style approach or by utilizing the collapse widget in Bootstrap?

Answer №1

After some reflection, I devised a new strategy and wanted to share it with those who are interested.

To begin, let's establish the states required for transition. I'll demonstrate this using only two collapse panels instead of the original three. It may seem like a lot of code, but the solution is valuable enough to warrant sharing.

Routing app.js

        .state('home.checkout', {
            url: 'checkout',
            views: {
                '@home': {
                    templateUrl: 'views/partials/generic/checkout-process/order-checkout-root.html'
                }
            }
        })

        .state('home.checkout.shoppingcart', {
            url: '^/shoppingcart',
            views: {
                '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="aad9c2c5dadac3c4cd87c9cbd8deeac2c5c7cf84c9c2cfc9c1c5dfde">[email protected]</a>': {
                    templateUrl: 'views/partials/generic/checkout-process/shoppingcart/shopping-cart-partial.html',
                    controller: 'ShoppingCartController'
                },
                '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b4dbc6d0d1c699d7dbdaf2dd69c4dce3dfd7dcced6dddacbcedfd0c6cacec8cdc7c6">[email protected]</a>' : {
                    templateUrl: 'views/partials/generic/checkout-process/closed-state.html',
                    controller: function($scope) {
                        $scope.page = {name: 'Order Confirmation'};
                        $scope.state = {name: 'home.checkout.confirm'};
                    }
                }
            }
        })

        .state('home.checkout.confirm', {
            url: '/confirmation',
            views: {
                '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1e6d76716e6e777079337d7f6c6a5e7671737b307d767b7d75716b6a">[email protected]</a>': {
                    templateUrl: 'views/partials/generic/checkout-process/closed-state.html',
                    controller: function($scope) {
                        $scope.page = {name: 'Shopping Cart'};
                        $scope.state = {name: 'home.checkout.shoppingcart'};
                    }
                },
                '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="abc4d9cfced986c8c4c5cdc2d9c6cadfc2c4c5ebc3c4c6ce85c8c3cec8c0c4dedf">[email protected]</a>': {
                    templateUrl: '../views/partials/generic/checkout-process/confirmation/order-confirmation-partial.html',
                    controller: 'OrderConfirmationController'
                }
            }
        })

HTML order-checkout-root.html

<div class="row checkout-process">
    <section class="col-sm-8 col-md-8 col-lg-8 panel-group" id="accordion">
        <div class="shopping-cart panel panel-default" ui-view="shopping-cart" autoscroll="false"></div>
        <div class="order-confirmation panel panel-default" ui-view="order-confirmation" autoscroll="false"></div>
    </section>
</div>

closed-state.html

<article class="col-sm-12 col-md-12 col-lg-12 panel-heading closed-state">
    <h4 class="panel-title">
        <a ui-sref="{{state.name}}">
            {{page.name}}
        </a>
    </h4>
</article>

order-confirmation-partial.html

I will only include this one and not the other partial as its the same idea.

<div class="order-confirmation-page row">
    <div class="panel-heading">
        <h4 class="panel-title">Order Confirmation</h4>
    </div>

    <div class="panel-collapse collapse" kx-collapse-toggler data-toggle="collapse">
        <div class="panel-body">
            <!--Code for the collapse body goes here-->
        </div>
    </div>
</div>

Whats important from this last partial is to note the inclusion of the directive

kx-collapse-toggler

This is where we do our work and the most interesting part of the code

collapseTogglerDirective.js

'use strict';

angular.module('App.Directives.CollapseToggler', [])

    .directive('kxCollapseToggler', function ($rootScope, $state, $q, $timeout) {

        var linker = function(scope, element) {

            var
                collapse = $q.defer(),
                changeEventStarted = false
            ;

            //Expand the panel on directive instantiation
            $timeout(function() {
                $(element).collapse('show');
            }, 300);


            $rootScope.$on('$stateChangeStart', function(event, toState) {
                //Check to make sure we arent in the middle of a $stateChangeEvent
                if(changeEventStarted) {
                    return;
                }
                //Stop the state transition
                event.preventDefault();

                //Collapse the panel
                $(element).collapse('hide');

                //Wait for the panel to collapse completely
                collapse.promise.then(function() {
                    changeEventStarted = true;
                    //Then transiton the state
                    $state.transitionTo(toState);
                });
            });

            //Event listener for the collapse completed
            $(element).on('hidden.bs.collapse', function() {
                collapse.resolve();
            });
        };

        return {
            restrict: 'A',
            link: linker
        };
    });

In short what we do here is:

  1. Setup a promise to know when we can transition again.
  2. Intercept the $stateChangeStart event and stop it from happening.
  3. Then we collapse the panel we are interested in
  4. When the collapse is finished bootstrap fires an event saying I am done collapsing which we listen for and in turn resolve the promise
  5. When the promise is resolved we can safely transition to the next state

I hope that this isnt too much to follow, but if you do the potential it has for other kinds of animation is pretty great.

I am working on putting together a plunker so its possible to see the animation.

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

Testing routes in AngularJS with "resolve" functionality

I'm facing a challenge while trying to unit test one of my routes, receiving the dreaded "Error: Unexpected request" message. The route in question includes a "resolve" parameter and is structured like this: when('/Users', { ...

Using Ajax to preview images results in displaying a broken image icon

I am currently working on implementing an image preview function using Ajax. As I was experimenting, a couple of questions came to my mind: Once the Ajax has been executed, is the actual image uploaded to the server or just an array containing strings l ...

Unusual shadow cast by the box's silhouette

I am currently facing an issue with a box and its shadow. When I close the box, a different shadow lingers behind. I have tried troubleshooting this problem but cannot pinpoint the source. I have included the relevant code files in the specified folders. I ...

Tips for correctly sending the response code from a Node.js API

I have a straightforward node-based API that is responsible for parsing JSON data, saving it into a Postgres database, and sending the correct response code (e.g., HTTP 201). Here is an excerpt of my code: router.route('/customer') .post(fu ...

The <mat-radio-button> component does not have a value accessor specified

When working with HTML and Angular, I encountered the following issue: <mat-radio-group> <mat-radio-button [(ngModel)]="searchType"> And (Narrower search) </mat-radio-button> <mat-radio-button [(ngModel)]="searchType"&g ...

Issue with Ionic displaying data from AngularJS $http.get request

Having just started learning AngularJS, I have followed tutorials on YouTube and read the documentation, but I am struggling to display data from an API using the $http.get() request. Here is my JavaScript and HTML code: var exampleApp= angular.modul ...

I have noticed that the Javascript code is being loaded prior to the completion of my HTML/CSS page for some unknown

I am completely new to the world of web development. I'm encountering an issue where my JavaScript code (specifically alerts) is loading before my HTML/CSS page has fully loaded. I've been using sample alerts to test this out, and ideally, they s ...

Steps to create a submit button that is linked to a URL and includes an image

I'm attempting to convert my submit button into an image that, when clicked, redirects to another page. Currently, the submit button is functional but lacks the desired image and href functionality. <input type="submit" name="submit" alt="add" o ...

In Vue.js, modifying a parent component variable using $parent does not update the interpolation syntax

Child component <template> <div> <h3>Child Component</h3> <div> <button @click="changeValue()">Update Parent Value</button> </div> </div> </template> <script> export ...

Guide to dynamically updating a textarea in vue.js by incorporating data from several inputs

Is there a way to update a textarea based on multiple inputs using jQuery and vue.js? I have successfully implemented the jQuery part with line breaks, but when I try to display the value of the textarea elsewhere using vue.js, it doesn't seem to work ...

Having trouble modifying a nested object array within a treeview component in Reactjs

Thanks for your help in advance! Question: I'm having trouble updating a nested object of an array in a treeview using Reactjs. Please refer to the code and sandbox link below: https://codesandbox.io/s/cocky-leakey-ptjt50?file=/src/Family.js Data O ...

Struggling with this CSS is really getting to me

I am struggling to create a modal with the tools and close button appearing on the same line. It should look similar to the image below: var modal = document.getElementById('myModal'); var btn = document.getElementById("myBtn"); var span = doc ...

Utilize text wrapping to ensure a fixed maximum height for content display

I am in need of a div that contains text spanning multiple lines, with both a fixed width and a maximum height. Currently, I have applied the CSS property overflow: hidden;. However, my issue arises when the last line of text exceeds the maximum height of ...

Navigating through Angularjs Routeprovider within a subfolder of MODx

I have an AngularJS app running on my MODx website. There is a page with a URL like localhost/www.mysite.com/angularapp/ where the app is located. The URLs inside the app look like localhost/www.mysite.com/angularapp/category/1 The base href is set to ...

The array is coming back empty after attempting to add objects to it

My Node JS and Express JS controller code is below: const UserComment = require(".../model/UserComment"); router.post("/get/comments", async (req, res) =>{ try{ let currentUserID = req.body.userID; let myUserComment = await UserComment.find({userID: cu ...

Determining the total number of current connections using JavaScript, jQuery, and Selenium WebDriver

I need your assistance as I've encountered a roadblock. In my automated tests using Selenium WebDriver + Java, I rely on the following code snippet to check for active background AJAX connections: private boolean hasNoActiveConnections() { retur ...

transmitting comfort through events

To enhance my application, I am working on publishing a solace message to a topic and then passing it along to another part of the app for further processing using events. This entire process is happening within a single node.js process. While I understand ...

Tips for preventing repetition of code in multiple entry points in Rollup

My goal is to use rollup to process a group of input files and generate multiple output files in the dist directory that all have some common code shared between them. Below is my current rollup configuration: import path from 'path'; import pat ...

Instant feedback from dynamically generated text boxes

As a Python developer, I am venturing into the realm of HTML. Currently, I am working on an internal tool that enables users to create directories for various projects. To achieve this, I have implemented a method for dynamically adding and removing text b ...

Use jQuery to switch the class of the designated DIV in the showcasing slider

Check out this jQuery slider example where I have two Divs - DIV ONE and DIV TWO. Can you help me figure out how to: 1) Automatically change the class of DIV ONE from "test" to "testc" when Slide 1 is displayed by the slider. 2) Automatically update DIV ...