2

I'm implementing the a slide toggle for a nested menu in AngularJS. I've been using the code from the following page: https://blog.assaf.co/native-slide-toggle-for-angularjs-1-4-x/

I'm having only one problem with it though. I'm trying to implement this on a sidenav menu with nested submenues, built with <ul> and <li> elements. After having toggled a menu item that has submenu, and I later toggle the submenu of that same menu button, the <ul> element of that menu button doesn't slide in. The height remains the same.

Also, if I keep the submenu closed, and open only the top menu item, the height of the <ul> element remains the same when I toggle the submenu, meaning that the submenu doesn't show until I toggle it open (though remaining invisible), then close the top menu button, then open it again.

Please see it in action here: https://codepen.io/marcus-edensky/pen/MBoWZJ

Here's my JavaScript code:

app.controller('myCtrl', function($scope, $mdSidenav) {
});

app.animation('.slide-toggle', ['$animateCss', function($animateCss) {
    var lastId = 0;
    var _cache = {};

    function getId(el) {
        var id = el[0].getAttribute("data-slide-toggle");
        if (!id) {
            id = ++lastId;
            el[0].setAttribute("data-slide-toggle", id);
        }
        return id;
    }
    function getState(id) {
        var state = _cache[id];
        if (!state) {
            state = {};
            _cache[id] = state;
        }
        return state;
    }

    function generateRunner(closing, state, animator, element, doneFn) {
        return function() {
            state.animating = true;
            state.animator = animator;
            state.doneFn = doneFn;
            animator.start().finally(function() {
                if (closing && state.doneFn === doneFn) {
                    element[0].style.height = '';
                }
                state.animating = false;
                state.animator = undefined;
                state.doneFn();
            });
        }
    }

    return {
        addClass: function(element, className, doneFn) {
            if (className == 'ng-hide') {
                var state = getState(getId(element));
                var height = (state.animating && state.height) ? 
                    state.height : element[0].offsetHeight;

                var animator = $animateCss(element, {
                    from: {height: height + 'px'},
                    to: {height: '0px'}
                });
                if (animator) {
                    if (state.animating) {
                        state.doneFn = 
                          generateRunner(true, 
                                         state, 
                                         animator, 
                                         element, 
                                         doneFn);
                        return state.animator.end();
                    }
                    else {
                        state.height = height;
                        return generateRunner(true, 
                                              state, 
                                              animator, 
                                              element, 
                                              doneFn)();
                    }
                }
            }
            doneFn();
        },
        removeClass: function(element, className, doneFn) {
            if (className == 'ng-hide') {
                var state = getState(getId(element));
                var height = (state.animating && state.height) ?  
                    state.height : element[0].offsetHeight;

                var animator = $animateCss(element, {
                    from: {height: '0px'},
                    to: {height: height + 'px'}
                });

                if (animator) {
                    if (state.animating) {
                        state.doneFn = generateRunner(false, 
                                                      state, 
                                                      animator, 
                                                      element, 
                                                      doneFn);
                        return state.animator.end();
                    }
                    else {
                        state.height = height;
                        return generateRunner(false, 
                                              state, 
                                              animator, 
                                              element, 
                                              doneFn)();
                    }
                }
            }
            doneFn();
        }
    };
}]);



(function() {
    var app = angular.module('app', ['ngAnimate']);

    app.animation('.slide-toggle', ['$animateCss', function($animateCss) {
        return {
            addClass: function(element, className, doneFn) {
                if (className == 'ng-hide') {
                    var animator = $animateCss(element, {                    
                        to: {height: '0px'}
                    });
                    if (animator) {
                        return animator.start().finally(function() {
                            element[0].style.height = '';
                            doneFn();
                        });
                    }
                }
                doneFn();
            },
            removeClass: function(element, className, doneFn) {
                if (className == 'ng-hide') {
                    var height = element[0].offsetHeight;
                    var animator = $animateCss(element, {
                        from: {height: '0px'},
                        to: {height: height + 'px'}
                    });
                    if (animator) {
                     return animator.start().finally(doneFn);
                    }
                }
                doneFn();
            }
        };
    }]);
})();

Here's the HTML - a simple :

<ul class="menu-toggle-list">
    <li><a md-ink-ripple ng-init="showMenu2 = true" ng-click="showMenu2 = !showMenu2" class="md-button menuSub">Tours</a>
        <ul ng-show="showMenu2" class="slide-toggle">
            <li><a md-ink-ripple ng-init="showMenu3 = true" ng-click="showMenu3 = !showMenu3" class="md-button menuSub">Group tours</a>
            <ul ng-show="showMenu3" class="slide-toggle">
                <li><a md-ink-ripple href="URL/TOURS/GROUP-TOURS/PLANNING/" class="md-button">Planning</a></li>
                <li><a md-ink-ripple href="URL/TOURS/GROUP-TOURS/TYPES/" class="md-button">Types</a></li>
            </ul>
            </li>
        </ul>
    </li>
    <li><a md-ink-ripple ng-init="showMenu6 = true" ng-click="showMenu6 = !showMenu6" class="md-button menuSub">Users</a>
        <ul ng-show="showMenu6" class="slide-toggle">
            <li><a md-ink-ripple href="URL/TOURS/GROUP-TOURS/" class="md-button">Staff</a></li>
            <li><a md-ink-ripple href="URL/TOURS/GROUP-TOURS/" class="md-button">Clients</a></li>
        </ul>
    </li>
</ul>

Here's the CSS:

.slide-toggle {
  overflow: hidden;
  transition: all 0.25s; 
}

1 Answer 1

2
+50

The parent still holds the height after the animation. You have to remove it.

Add element[0].style.height = 'auto' after animation is done.In the generateRunner function at line 36 after the if

function generateRunner(closing, state, animator, element, doneFn) {
  return function () {
    state.animating = true;
    state.animator = animator;
    state.doneFn = doneFn;
    animator.start().finally(function () {
      if (closing && state.doneFn === doneFn) {
        element[0].style.height = '';
      }
      element[0].style.height = 'auto'; // <----- HERE
      state.animating = false;
      state.animator = undefined;
      state.doneFn();
    });
  }
}

Hope this helps. Good luck!

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.