Search code examples
javascriptangularjsangular-bootstrap

how to use controller $scope in directive template


I am trying to implement Bootstrap Carousel with custom indicator using Angular, Carousel works well, but I am not able customize indicator.

var App = angular.module('App', ['ngAnimate', 'ui.bootstrap', 'ngTouch']);
App.controller('Carousel', function($scope, $http, $element){
    $scope.myInterval = 5000;
    $scope.noWrapSlides = false;
    var slides = $scope.slides = [];

    $scope.slides = [{"img":"images\/bg-slider1.jpg","name":"GIFTS","class":"pinkbg"},{"img":"images\/bg-slider2.jpg","name":"FASHION","class":"ltgreen"},{"img":"images\/bg-slider1.jpg","name":"ASTROLOGY","class":"voilet"},{"img":"images\/bg-slider2.jpg","name":"ORGANIC","class":"green"},{"img":"images\/bg-slider1.jpg","name":"SPORTS","class":"yellow"},{"img":"images\/bg-slider2.jpg","name":"APPLIANCES","class":"grey"}];
    $scope.getNameByIndex = function(index){
        return $scope.slides[index]['name'];
    }
});

angular.module("template/carousel/carousel.html", []).run(["$templateCache", "$http", function($templateCache, $http) {
  $templateCache.put("template/carousel/carousel.html",
    "<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\n" +
    "    <ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\n" +
    "        <li ng-repeat=\"slide in slides | orderBy:indexOfSlide track by $index\" ng-class=\"{active: isActive(slide)}\" ng-click=\"select(slide)\" class='{{slide.class}}'>{{slide.name}}</li>\n" +
    "    </ol>\n" +
    "    <div class=\"carousel-inner\" ng-transclude></div>\n" +
    "</div>\n" +
    "");
}]);

$scope.slides has additional attributes, name and class that needs to placed in indicator. how can I do that ?

See Plunker


Solution

  • To start off this caught my interest why the scope was not accessible. After a detailed analysis of the source code available on GitHub i found that the scope is being isolated in the library.

    Plukr Link

    On plunkr i have modified the library. File name : angular-bootstrap-modified.js

    Read below for details.

    See the below code chunk from Git Hub:

    .directive('carousel', [function() {
      return {
        restrict: 'EA',
        transclude: true,
        replace: true,
        controller: 'CarouselController',
        controllerAs: 'carousel',
        require: 'carousel',
        templateUrl: function(element, attrs) {
          return attrs.templateUrl || 'template/carousel/carousel.html';
        },
        scope: {
          interval: '=',
          noTransition: '=',
          noPause: '=',
          noWrap: '&'
        }
      };
    }])
    

    Newly created isolated scope variable for the directive inherits below data from its parent scope.

    {
       interval: '=',
       noTransition: '=',
       noPause: '=',
       noWrap: '&'
    }
    

    Finally found that its a limitation from the library itself.

    So i modified the library to achieve the solution. Below is what i changed

    Library code:

    scope: {
             interval: '=',
             noTransition: '=',
             noPause: '=',
             noWrap: '&',
             slidesData : '=slides'
        }
    

    and in HTML:

    <carousel interval="myInterval" no-wrap="noWrapSlides" slides="slides">
    

    After the modification, volla! now the directive isolated scope inherits the slides data too. Sweet!

    Info on Angular directives can be found - here

    Next tweaked the template code: All i did was change the slides reference to slideData

    angular.module("template/carousel/carousel.html", []).run(["$templateCache", "$http", function($templateCache, $http) {
      $templateCache.put("template/carousel/carousel.html",
        "<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\n" +
        "    <ol class=\"carousel-indicators\" ng-show=\"slidesData.length > 1\">\n" +
        "        <li ng-repeat=\"slide in slidesData | orderBy:indexOfSlide track by $index\" ng-class=\"{active: isActive(slide)}\" ng-click=\"select(slide)\" class='{{slide.class}}'>{{slide.name}} - {{$index}}</li>\n" +
        "    </ol>\n" +
        "    <div class=\"carousel-inner\" ng-transclude></div>\n" +
        "</div>\n" +
        "");
    }]);
    

    Good now i can see the the name and class on <ol></ol> tag element rendered.

    enter image description here

    But now the switching animation doesnt happen on <li> .Below is the routine that compares the scope instead of index(i donno why?)

    $scope.isActive = function(slide) {
         return self.currentSlide === slide;
    };
    

    I figured out by watching the expressions on my debugger console

    enter image description here

    Final Words:

    If you want to achieve it somehow.. You have to modify the library source itself. I would advice you to check out other sliders if you want customized sliders or use as is provided by the Angular Bootstrap library.


    Hope the above info explains the limitation. Happy Coding!


    Also GitHub issue logged


    Update on this issue

    Carousel customization will be implemented in the future versions as per the Github issue update