Something like Bootstrap collapsible with angular

Roland picture Roland · Feb 15, 2013 · Viewed 13.8k times · Source

I'm building a small app and I'm using AngularJS. Inside the app I need a collapsible element and using Twitter Bootstrap would be as easy as adding the library and some tags on my target element and the trigger.

But I'm trying not to load other external libraries like bootstrap or any other, so I was trying to achieve the same behavior with Angular :

$scope.collapse = function(target) {

        var that = angular.element(document).find(target),

            transitioned = {
                'WebkitTransition' : 'webkitTransitionEnd',
                'MozTransition'    : 'transitionend',
                'OTransition'      : 'oTransitionEnd otransitionend',
                'msTransition'     : 'MSTransitionEnd',
                'transition'       : 'transitionend'
            },

            _transitioned = transitioned[ Modernizr.prefixed('transition') ],

            height = that.outerHeight(true);

        if (angular.element(that).hasClass("in")) {
            that.height(0);
        } else {
            that.height(height);
        };

        that.on(_transitioned, function() {
            that.toggleClass("in");
        });
    };

As you can see I'm trying to transition the height ( as the element has a transition on the height ) and in the end just add the class in. But that isn't working very well because if I'm listening on the transition end it will trigger on any transition end inside that element.

I would need some help with this, how can I rewrite the bootstrap collapsible just with angular ? I don't need the events that bootstrap has like on shown, hidden or show, hide, I just need to trigger a simple collapse of the element with transition and keep my elements height dynamic ( I don't want a fixed height, otherwise I would just use CSS to achieve the collapse ). I just need to be pinpointed in the right direction :)

Answer

Ben Lesh picture Ben Lesh · Feb 15, 2013

Seems like you want to just collapse something with CSS3 transitions?

Well, you can do that, but the controller is the wrong place to be doing that. You should do that with directives or in a custom directive. Fortunately, you can do it with the Angular native directives, such as ng-class.

HTML:

<button ng-click="open = !open">Toggle</button>    
<div ng-class="{ showMe: open }" class="collapsable">
  <h3>This should collapse</h3>
</div>

And most importantly, your CSS:

  .collapsable {
    display: inline-block;
    overflow: hidden;
    height: 0;
    transition: height 1s;
    -webkit-transition: height 1s;        
    -moz-transition: height 1s;        
    -o-transition: height 1s;         
  }
  .collapsable.showMe {
    height: 100px;
  }

And here is a plunker of it working.

Important to note, CSS3 transitions will not work in all browsers. Particularly in IE. In the end, I think you'd probably be better off using a plugin that someone else already made, and then leveraging it in a custom directive that watched some boolean value.

I hope that helps.


EDIT

height: auto does not work with CSS Transitions (at least as of the time of this post). So, this is why you'll really want to use someone else's plugin, rather than reinvent the wheel. Even if it's just JQuery's animate() method. The psuedo-code for rolling your own directive would be something like so: (presuming you're using JQuery)

app.directive('collapseWhen', function () {
   return function(scope, elem, attr) {
     scope.$watch(attr.collapseWhen, function(val) {
        var clone = elem.clone().appendTo('body');
        var h = clone.height();
        clone.remove();
        scope.animate({ height: (val ? h : 0) + 'px' }, 1000);
     }
   }
});

and then you'd use it like:

<button ng-click="foo = !foo">Toggle</button>
<div collapse-when="foo">

Again, the above is psuedo-code, I have no idea if it will work or not, it's just to give you an idea to follow if you really want to roll your own.