14
<div class="owl-carousel">
    <div ng-repeat="items in itemlist"> 
        <a href="series.html"><img ng-src="{{items.imageUrl}}" /></a>
    </div>
    <div> 
      <a href="series.html"><img src="http://placehold.it/350x150" /></a>
    </div>
 </div>

View carousel here: Owl-carousel2

I'm running into an issue where whenever the ng-repeat directive is applied to carousel the items are stacked vertically instead of being layout horizontally.

If I leave out ng-repeat and use static items then it works as it should.

Is there a directive I can write and apply to owl-carousel to maintain the layout?

Also what is about ng-repeat that is causing the carousel to break?

Is angular somehow stripping the owl-carousel classes applied to the carousel?

Note* If build the list manually then iterate through and append the elements using :

var div = document.createElement('div');
var anchor = document.createElement('a');
var img = document.createElement('img');            
.....       
carousel.appendChild(div);

then call the owl.owlCarousel({..}) It works, not sure if this is the best work around because ng-repeat makes everything bit easier.

I discovered a hack , if I wrap the owl init in a timeout then ng-repat works.

setTimeout(function(){
      ...call owl init now  
},1000);

<link rel="stylesheet" href="css/owl.carousel.css"/>
<link rel="stylesheet" href="css/owl.theme.default.min.css"/>

.....
    <script src="/js/lib/owl.carousel.min.js"></script> 
        <script>
             $(document).ready(function() {
               var owl = $('.owl-carousel');
               owl.owlCarousel({
                 .....
               });
               owl.on('mousewheel', '.owl-stage', function(e) {
                 if (e.deltaY > 0) {
                   owl.trigger('next.owl');
                 } else {
                   owl.trigger('prev.owl');
                 }
                 e.preventDefault();
               });
             })

        </script>
Fabii
  • 3,820
  • 14
  • 51
  • 92
  • What code are you using to load the owl carousel? This is almost certainly a load order problem. i.e the ng-repeat hasn't manipulated the DOM before the owl carousel builds the carousel out of it. – rwacarter Jan 16 '15 at 16:47
  • @rwacarter check out the content I added above – Fabii Jan 16 '15 at 16:50
  • @rwcarter How do I get around this, if that is in fact the issue? – Fabii Jan 16 '15 at 16:51
  • Should I build my list then call owl-carousel build once the list is populated? – Fabii Jan 16 '15 at 16:54
  • Yes exactly. Instead of `owl.owlCarousel()` when the DOM has loaded (`$(document).ready(function() {`), add it into the angular controller. – rwacarter Jan 16 '15 at 17:03
  • This github conversation may also be of use: https://github.com/OwlFonk/OwlCarousel/issues/179 – rwacarter Jan 16 '15 at 17:04
  • hmm tried that , it only seems to work if I manually create the necessary elements , append them to the carousel container then call the owl init process. @rwacarter – Fabii Jan 16 '15 at 17:18
  • 2
    setTimeout(function(){ ...call owl init now },1000); does the trick after the list is loaded – Fabii Jan 16 '15 at 19:12
  • Hope this will help somebody. It can handle multiple carousel's, you can fire events and when re-rendered will go to the last element/current element. https://gist.github.com/daniel3d/829fd967d4436af220082b76c3153362 – Daniel Yovchev Nov 02 '16 at 23:46

4 Answers4

26

Was able to modify a directive from DTing on another post to get it working with multiple carousels on the same page. Here is a working plnkr

-- Edit -- Have another plnkr to give an example on how to add an item. Doing a reinit() did not work cause any time the owl carousel is destroyed it loses the data- elements and can never initialize again.

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {
  $scope.items1 = [1,2,3,4,5];
  $scope.items2 = [1,2,3,4,5,6,7,8,9,10];
}).directive("owlCarousel", function() {
    return {
        restrict: 'E',
        transclude: false,
        link: function (scope) {
            scope.initCarousel = function(element) {
              // provide any default options you want
                var defaultOptions = {
                };
                var customOptions = scope.$eval($(element).attr('data-options'));
                // combine the two options objects
                for(var key in customOptions) {
                    defaultOptions[key] = customOptions[key];
                }
                // init carousel
                $(element).owlCarousel(defaultOptions);
            };
        }
    };
})
.directive('owlCarouselItem', [function() {
    return {
        restrict: 'A',
        transclude: false,
        link: function(scope, element) {
          // wait for the last item in the ng-repeat then call init
            if(scope.$last) {
                scope.initCarousel(element.parent());
            }
        }
    };
}]);

Here is the HTML

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/owl-carousel/1.3.3/owl.carousel.min.css" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/owl-carousel/1.3.3/owl.theme.min.css" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/owl-carousel/1.3.3/owl.transitions.min.css" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/owl-carousel/1.3.3/owl.carousel.min.js" />
    <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.15/angular.js" data-semver="1.3.15"></script>
    <script data-require="jquery@2.1.3" data-semver="2.1.3" src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/owl-carousel/1.3.3/owl.carousel.min.js"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <data-owl-carousel class="owl-carousel" data-options="{navigation: true, pagination: false, rewindNav : false}">
      <div owl-carousel-item="" ng-repeat="item in ::items1" class="item">
        <p>{{::item}}</p>
      </div>
    </data-owl-carousel>
    <data-owl-carousel class="owl-carousel" data-options="{navigation: false, pagination: true, rewindNav : false}">
      <div owl-carousel-item="" ng-repeat="item in ::items2" class="item">
        <p>{{::item}}</p>
      </div>
    </data-owl-carousel>
  </body>

</html>
Community
  • 1
  • 1
JKOlaf
  • 475
  • 5
  • 6
  • Thanks for your answer. It's great. The fact the it's waiting on the last item to be looped and then initialize the carousel, makes a big difference when dealing with async events / loading external content. – msaad Nov 04 '15 at 13:42
  • owl dots were not displayed by using this – devanathan May 23 '16 at 14:01
  • What should i do if each item in my array is a separate GET request and after each response the `if(scope.$last)` is true? all the items are outside of the carousel, how can this be fixed? – FireBrand Jun 09 '16 at 15:08
  • I want to reinit the carousel. I am applying in scope.initcarousel funtion as "scope.$watchCollection('item', function(newValue, oldValue) { if (newValue){ element.data('owlCarousel').reinit({items : 5, pagination: false, rewindNav : false,responsiveClass:true, itemsCustom:[[768,5]]}); } });" but it's giving empty item on each reinit. – Ammar Hayder Khan Jun 10 '16 at 12:01
  • @FireBrand instead of the scope.$last to initialize you could rework the directive to add the new item. I will try a few things and see what I come up with. – JKOlaf Jun 13 '16 at 00:33
  • @JKOlaf please the last item grows in height bigger than the others. Please how can i address that. Your solution works for me but the last item increases in heights – Nuru Salihu Jun 18 '16 at 03:58
  • 1
    I wasn't having any success with owl.reinit() but i did have success with adding a single item. I leave the watchers off on ::items1 and ::items2 and just push a new number to the array and add the html through a function. Updated plnkr can be found here: http://plnkr.co/edit/qAnXhA7yVyZDQUq6sXGY?p=preview – JKOlaf Jun 19 '16 at 17:14
  • This solution works but it always takes auto width/height for the images which raises issue when we need smaller images in the Carousel. – Dash May 24 '17 at 09:26
  • Thank you for passing parent element in initCarousel and then accessing its attr. I was banging my head because child directive was calling wrong parent initcarousel (when directive was used on multiple elements) and hence attr passed to parent were also lost – artsnr Nov 18 '20 at 14:44
6

I tried fiddling around with different directives but had no luck until I discovered this, it might a bit of a hack fix, but it works nonetheless.

Here is my div setup:

<div ng-repeat="item in mediaitems">
  <img ng-src="item.imageurl" />
</div>

$scope.mediaitems is generated using ajax call. I found that if I delayed the owl initialization until my list was populated then it would render it properly. Also if you decide you want update you list dynamically just call the setupcarousel function (look below) after the list has been populated and it will re-init the carousel.

Note that carousel init is in an external file within an anonymous function. That's just how I choosed to set it up, you can have yours in-line or however you please.

In my controller I had something like this :

$scope.populate = function(){
     $timeout(function(){   
      $scope.mediaitems  = returnedlist;   //list of items retrun from server
       utils.setupcarousel();              //call owl initialization
     });
};

var utils = (function(){
    var setupcarousel = function(){
        console.log('setting up carousel..');
         var owl = $('.owl-carousel');
         if(typeof owl.data('owlCarousel') != 'undefined'){
             owl.data('owlCarousel').destroy();
             owl.removeClass('owl-carousel');
         }

        owl.owlCarousel({
            loop: false,
            nav: true,
            margin: 10,
            responsive: {
              0: {items: 3 },
              600: {items: 5},
              960: { items: 8},
              1200:{items: 10},
              1600:{items: 12}
            }
          });
    };

    return{
      ....
    }

})();
Fabii
  • 3,820
  • 14
  • 51
  • 92
  • I'm have a very similar issue, and your approach here makes a lot of sense to me. However, this line "owl.data('owlCarousel').destroy();" doesn't work - it's telling me that it can't remove a property of "undefined". Help? – Jonathan Jun 30 '15 at 11:11
  • 1
    Nevermind, found the issue! For whatever reason, the line "if(typeof owl.data('owlCarousel') != 'undefined')" was not catching undefined objects for me, so instead I just broke it out into "var owlData = owl.data('owlCarousel')" before the if, and then did "if(owlData === undefined)" and it worked like a charm. Thanks! – Jonathan Jun 30 '15 at 11:30
1

The Angular UI Team has put together a set of bootstrap components implemented as angular directives. They are super sleek and fast to implement, and because they are directives, you don't run into issues with using jquery in an angular project. One of the directives is a carousel. You can find it here and here. I messed around with carousels for a long time with angular. I got the owl to work after some annoying tinkering, but AngularUI's implementation is much easier.

Patrick Motard
  • 2,650
  • 2
  • 14
  • 23
0

This is the same answer as mentioned by JKOlaf. However I've added responsive behaviour to it which provides better UX. 2 major improvements: 1. Fully responsive code resulting better UX in different devices. 2. The "autoHeight" property is handled now resulting smaller thumbnail of the images.

Code goes below:

 
Was able to modify a directive from DTing on another post to get it working with multiple carousels on the same page. Here is a working plnkr

-- Edit -- Have another plnkr to give an example on how to add an item. Doing a reinit() did not work cause any time the owl carousel is destroyed it loses the data- elements and can never initialize again.

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {
  $scope.items1 = [1,2,3,4,5];
  $scope.items2 = [1,2,3,4,5,6,7,8,9,10];
}).directive("owlCarousel", function() {
    return {
        restrict: 'E',
        transclude: false,
        link: function (scope) {
            scope.initCarousel = function(element) {
              // provide any default options you want
                var defaultOptions = {
                };
                var customOptions = scope.$eval($(element).attr('data-options'));
                // combine the two options objects
                for(var key in customOptions) {
                    defaultOptions[key] = customOptions[key];
                }
                defaultOptions['responsive'] = {
                    0: {
                        items: 1
                    },
                    600: {
                        items: 3
                    },
                    1000: {
                        items: 6
                    }
                };
                // init carousel
                $(element).owlCarousel(defaultOptions);
            };
        }
    };
})
.directive('owlCarouselItem', [function() {
    return {
        restrict: 'A',
        transclude: false,
        link: function(scope, element) {
          // wait for the last item in the ng-repeat then call init
            if(scope.$last) {
                scope.initCarousel(element.parent());
            }
        }
    };
}]);

You can change the responsive item counts as per your requirement. Set it to a smaller value for larger thumbnail of images. Hope this will help somebody and Thanks to the original answer provider.

Dash
  • 804
  • 1
  • 9
  • 16
  • Can something like this be done with nested ng-if statements, I have an array of videos to embed as well as an array of pictures. This gets tricky when you get to the owlCarouselItem when you are evaluating `scope.$last` – Zack Herbert Aug 08 '17 at 20:21
  • https://stackoverflow.com/questions/45594682/owl-carousel-populated-my-multiple-ng-repeats – Zack Herbert Aug 10 '17 at 16:05