Applying angularjs ng-repeat to owl-carousel

39,031

Solution 1

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="[email protected]" src="https://code.angularjs.org/1.3.15/angular.js" data-semver="1.3.15"></script>
    <script data-require="[email protected]" 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>

Solution 2

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{
      ....
    }

})();

Solution 3

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.

Share:
39,031
Fabii
Author by

Fabii

Application developer. Aspiring world traveler. Student of knowledge and other good things.

Updated on September 08, 2020

Comments

  • Fabii
    Fabii almost 4 years
    <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>
    
  • Jonathan
    Jonathan about 9 years
    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
    Jonathan about 9 years
    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!
  • msaad
    msaad over 8 years
    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.
  • devanathan
    devanathan about 8 years
    owl dots were not displayed by using this
  • FireBrand
    FireBrand about 8 years
    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?
  • Ammar Hayder Khan
    Ammar Hayder Khan about 8 years
    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.
  • JKOlaf
    JKOlaf about 8 years
    @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.
  • Nuru Salihu
    Nuru Salihu about 8 years
    @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
  • JKOlaf
    JKOlaf about 8 years
    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: plnkr.co/edit/qAnXhA7yVyZDQUq6sXGY?p=preview
  • Dash
    Dash about 7 years
    This solution works but it always takes auto width/height for the images which raises issue when we need smaller images in the Carousel.
  • Zack Herbert
    Zack Herbert almost 7 years
    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
    Zack Herbert almost 7 years
  • artsnr
    artsnr over 3 years
    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