Background Slideshow with AngularJS and Bootstrap

As part of a project we wanted to have the front page with a nice rotating background for the jumbotron. There are a number of carousel components and scripts that can be easily found online but mostly they use the img tag and/or require a root absolute div which means it won’t automatically resize to the jumbotron content. I wanted a jumbotron that would resize to the content and also provide a nice seamless transition for the images. So, I sat down and rolled my own.

Firstly you need to set up a jumbotron component:

.jumbotron-slideshow {
    position: relative;
    background-color: transparent;  // replace the standard bootstrap background color

    .slideshow {
        background-size: cover;
        background-repeat: no-repeat;
        background-position: 50% 50%;
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        
        /* Layer the images so that the visible one is below all the others,
         * but the previously active one fades out to reveal the visible one
         * below */
        transition: opacity 1s;
        opacity: 0;
        
        &.visible {
            transition: none;
            opacity: 1;
            z-index: -1;
        }
    }   
}       

And then the HTML:

<div class="jumbotron jumbotron-slideshow">
    <div ng-bg-slideshow="[ 'images/bg1.jpg', 'images/bg2.jpg', ... ]" interval=5000></div>

    ... content that you want ...

Create the angular template to generate the image divs:

<div ng-repeat="img in images"
        class="slideshow" ng-class="{ visible: active_image == $index }" ng-style="{ 'background-image': 'url(' + img + ')' }">
    </div>  

And finally the Angular component:

app.directive("ngBgSlideshow", function($interval) {
    return {
        restrict: 'A',
        scope: {
            ngBgSlideshow: '&',
            interval: '=',
        },
        templateUrl: 'views/components/slideshow.html',
        link: function( scope, elem, attrs ) {
            scope.$watch( 'ngBgSlideshow', function(val) {
                scope.images = val();
                scope.active_image = 0;
            });

            var change = $interval(function() {
                scope.active_image++;
                if( scope.active_image >= scope.images.length )
                    scope.active_image = 0;
            }, scope.interval || 1000 );
        
            scope.$on('$destroy', function() {
                $interval.cancel( change );
            });
        }
    };  
});         

Note: If you want to be able to programatically change the interval you’ll need to add a watch that recreates the interval when the interval attribute changes.