How do you Bind to Angular-UI's Carousel Slide

2020-01-29 05:03发布

I'm using Angular-UI's carousel and I need to tell my google charts to redraw after they have slid into view. In spite of what I've read, I can't seem to hook into the event.

See my attempt: http://plnkr.co/edit/Dt0wdzeimBcDlOONRiJJ?p=preview

HTML:

<carousel id="myC" interval="myInterval">
  <slide ng-repeat="slide in slides" active="slide.active">
    <img ng-src="{{slide.image}}" style="margin:auto;">
    <div class="carousel-caption">
      <h4>Slide {{$index}}</h4>
      <p>{{slide.text}}</p>
    </div>
  </slide>
</carousel>

On document load:

$('#myC').live('slid.bs.carousel', function (event) { console.log("slid"); } );

It should work something like this: http://jsfiddle.net/9fwuq/ - non-angular-ui carousel

Perhaps there is a more Angular way to hook into the fact that my chart has slid into view?

6条回答
家丑人穷心不美
2楼-- · 2020-01-29 05:17

AngularUI Bootstrap has changed naming conventions for controllers as thery have prefixed all of their controllers with prefix uib, so below is the updated solution of the original solution provided by runTarm:

Angular:

.directive('onCarouselChange', function($parse) {
    return {
        require: '^uibCarousel',
        link: function(scope, element, attrs, carouselCtrl) {
            var fn = $parse(attrs.onCarouselChange);
            var origSelect = carouselCtrl.select;
            carouselCtrl.select = function(nextSlide, direction, nextIndex) {
                if (nextSlide !== this.currentSlide) {
                    fn(scope, {
                        nextSlide: nextSlide,
                        direction: direction,
                        nextIndex: this.indexOfSlide(nextSlide)
                    });
                }
                return origSelect.apply(this, arguments);
            };
        }
    };
});

Angular with TypeScript:

module App.Directive {

    export class CarouselChange implements ng.IDirective {

        public require: string = '^uibCarousel';

        constructor(private $parse: ng.IParseService) { }

        public link: ng.IDirectiveLinkFn = (scope: ng.IScope, element: ng.IAugmentedJQuery, attributes: any, carouselCtrl: any) => {
            var fn = this.$parse(attributes.carouselChange);
            var origSelect = carouselCtrl.select;
            carouselCtrl.select = function(nextSlide, direction) {
                if (nextSlide !== this.currentSlide) {
                    fn(scope, {
                        nextSlide: nextSlide,
                        direction: direction
                    });
                }
                return origSelect.apply(this, arguments);
            };
        }

        static Factory(): ng.IDirectiveFactory {
            var directive: ng.IDirectiveFactory = ($parse: ng.IParseService) => new CarouselChange($parse);
            directive['$inject'] = ["$parse"];
            return directive;
        }
    }
}

Thanks,

查看更多
Emotional °昔
3楼-- · 2020-01-29 05:25

I managed to modify runTarm's answer so that it calls the callback once the slide has finished sliding into view (i.e. the sliding animation has finished). Here's my code:

.directive('onCarouselChange', function ($animate, $parse) {
    return {
        require: 'carousel',
        link: function (scope, element, attrs, carouselCtrl) {
            var fn = $parse(attrs.onCarouselChange);
            var origSelect = carouselCtrl.select;
            carouselCtrl.select = function (nextSlide, direction) {
                if (nextSlide !== this.currentSlide) {
                    $animate.on('addClass', nextSlide.$element, function (elem, phase) {
                        if (phase === 'close') {
                            fn(scope, {
                                nextSlide: nextSlide,
                                direction: direction,
                            });
                            $animate.off('addClass', elem);
                        }
                    });
                }
                return origSelect.apply(this, arguments);
            };
        }
    };
});

The secret lies in using $animate's event handler to call our function once the animation is finished.

查看更多
别忘想泡老子
4楼-- · 2020-01-29 05:30

There are 3 ways I can think of and that depends of your requirement.

Please see http://plnkr.co/edit/FnI8ZX4UQYS9mDUlrf6o?p=preview for examples.

  1. use $scope.$watch for an individual slide to check if it is become active.

    $scope.$watch('slides[0].active', function (active) {
      if (active) {
        console.log('slide 0 is active');
      }
    });
    
  2. use $scope.$watch with custom function to find an active slide.

    $scope.$watch(function () {
      for (var i = 0; i < slides.length; i++) {
        if (slides[i].active) {
          return slides[i];
        }
      }
    }, function (currentSlide, previousSlide) {
      if (currentSlide !== previousSlide) {
        console.log('currentSlide:', currentSlide);
      }
    });
    
  3. use a custom directive to intercept select() function of the carousel directive.

    .directive('onCarouselChange', function ($parse) {
      return {
        require: 'carousel',
        link: function (scope, element, attrs, carouselCtrl) {
          var fn = $parse(attrs.onCarouselChange);
          var origSelect = carouselCtrl.select;
          carouselCtrl.select = function (nextSlide, direction) {
            if (nextSlide !== this.currentSlide) {
              fn(scope, {
                nextSlide: nextSlide,
                direction: direction,
              });
            }
            return origSelect.apply(this, arguments);
          };
        }
      };
    });
    

    and use it like this:

    $scope.onSlideChanged = function (nextSlide, direction) {
        console.log('onSlideChanged:', direction, nextSlide);
    };
    

    and in html template:

    <carousel interval="myInterval" on-carousel-change="onSlideChanged(nextSlide, direction)">
    ...
    

Hope this help : )

查看更多
The star\"
5楼-- · 2020-01-29 05:34

And if you just want to start playing a video when the slide comes into view, and pause when it leaves:

JS

{# Uses angular v1.3.20 & angular-ui-bootstrap v0.13.4 Carousel #}
{% addtoblock "js" %}<script type="text/javascript">
angular.module('videoplay', []).directive('videoAutoCtrl', function() {
  return {
    require: '^carousel',
    link: function(scope, element, attrs) {
      var video = element[0];
      function setstate(visible) {
        if(visible) {
          video.play();
        } else {
          video.pause();
        }
      }
      // Because $watch calls $parse on the 1st arg, the property doesn't need to exist on first load
      scope.$parent.$watch('active', setstate);
    }
  };
});
</script>{% endaddtoblock %}
{% addtoblock "ng-requires" %}videoplay{% endaddtoblock %}

NOTE: Has additional bits for Django

HTML:

<carousel interval="15000">
  <slide>
   <video class="img-responsive-upscale" video-auto-ctrl loop preload="metadata">
    <source src=
...
查看更多
▲ chillily
6楼-- · 2020-01-29 05:41

Following the answer given by runTarm If you want to know the index of the next slide, you should add something like this:

 .directive('onCarouselChange', function ($parse) {
    return {
      require: 'carousel',
          link: function (scope, element, attrs, carouselCtrl) {
            var fn = $parse(attrs.onCarouselChange);
            var origSelect = carouselCtrl.select;
            carouselCtrl.select = function (nextSlide, direction,nextIndex) {
      if (nextSlide !== this.currentSlide) {
        fn(scope, {
          nextSlide: nextSlide,
          direction: direction,
          nextIndex:this.indexOfSlide(nextSlide)
        });
      }
      return origSelect.apply(this, arguments);
    };
  }
  };
})

Then, in the controller you just need to do this to catch the new index:

$scope.onSlideChanged = function (nextSlide, direction, nextIndex) {
    console.log(nextIndex);
}
查看更多
对你真心纯属浪费
7楼-- · 2020-01-29 05:42

here's an alternate method that uses controllers, somewhere between runTarm's #2 and #3.

original HTML + a new div:

<carousel id="myC" interval="myInterval">
  <slide ng-repeat="slide in slides" active="slide.active">
    <div ng-controller="SlideController"> <!-- NEW DIV -->
      <img ng-src="{{slide.image}}" style="margin:auto;">
      <div class="carousel-caption">
        <h4>Slide {{$index}}</h4>
        <p>{{slide.text}}</p>
      </div>
    </div>
  </slide>
</carousel>

the custom controller:

  .controller('SlideController',
    function($log, $scope) {
      var slide = $scope.slide;
      $scope.$watch('slide.active', function(newValue) {
        if (newValue) {
          $log.info("ACTIVE", slide.id);
        }
      });
    });
查看更多
登录 后发表回答