0

"use strict";
angular.module("appBanner", [])
    .controller('bannerCtrl', function ($scope) {
  
  //...

        $scope.currentIndex = 0;
        $scope.setCurrentSlideIndex = function (index) {
            $scope.currentIndex = index;
        }

        $scope.isCurrentSlideIndex = function (index) {
            return $scope.currentIndex === index;
        };

        $scope.prevSlide = function () {
            $scope.currentIndex = ($scope.currentIndex < $scope.slides.length - 1) ? ++$scope.currentIndex : 0;
        };

        $scope.nextSlide = function () {
            $scope.currentIndex = ($scope.currentIndex > 0) ? --$scope.currentIndex : $scope.slides.length - 1;
        };
  
  //...

    })
    .directive('banner', function () {
  return {
   templateUrl: 'views/partials/banner.html',
   restrict: 'E',
   transclude: false,
   replace:true,
   scope: false,
   link: function postLink(scope, element, attrs) {
    
    //var imgSlides = ...
    //var progressBar = ...
    //var slideTime = ...
    
    var tlMaster = initMasterTimeline(imgSlides, progressBar, slideTime);
    
    function updateCurrentIndex(index) {
     scope.$apply(function() {
       //***************************************************
       /*    How do I communicate the new current index back to my controller from this directive?
       /*
       /*****************************************************/
       });
    }
    
    function initMasterTimeline(imgSlides, progressBar, slideTime) {
     var tlAuto = initAutoTimeline(imgSlides, progressBar, slideTime);
     var tlBoatowners = initBoatownersTimeline(imgSlides, progressBar, slideTime);
     var tlCommercial = initCommercialTimeline(imgSlides, progressBar, slideTime);
          
     var tlMaster = new TimelineMax({repeat:-1});
     tlMaster.set(progressBar, {scaleX:0, transformOrigin:"left"})
     .add(tlAuto, "auto")
     .add(tlBoatowners, "boatowners")
     .add(tlCommercial, "commercial");
         
     return tlMaster;
    }
    
    function initAutoTimeline(imgSlides, progressBar, slideTime) {
     var stayTime= 10; //for now, can make each timeline as long as you want later
        
     var tlAuto = new TimelineLite({
      onUpdate:setProgress, 
      onUpdateParams:["{self}", progressBar]
     });
        
     
     tlAuto.set(imgSlides[0], {display: "block"})
     .to(progressBar, slideTime, {autoAlpha: 1}, 0)
     .to(imgSlides[0], slideTime, {autoAlpha:1}, 0)
     .to(imgSlides[0], slideTime, {autoAlpha:0}, stayTime)
     .to(progressBar, slideTime, {autoAlpha:0}, stayTime)
     .set(imgSlides[0], {display: "none", onComplete: updateCurrentIndex(1)})
        
     return tlAuto;
    }
    
    function initBoatownersTimeline(imgSlides, progressBar, slideTime) {
     var stayTime= 10; //for now, can make each timeline as long as you want later
    
     var tlBoatowners = new TimelineLite({
      onUpdate:setProgress, 
      onUpdateParams:["{self}", progressBar]
     });
        
     
       
     tlBoatowners.set(imgSlides[1], {display: "block"})
     .to(progressBar, slideTime, {autoAlpha: 1}, 0)
     .to(imgSlides[1], slideTime, {autoAlpha:1}, 0)
     .to(imgSlides[1], slideTime, {autoAlpha:0}, stayTime)
     .to(progressBar, slideTime, {autoAlpha:0}, stayTime)
     .set(imgSlides[1], {display: "none", onComplete: updateCurrentIndex(2)});
      
     return tlBoatowners; 
    }
    
    function initCommercialTimeline(imgSlides, progressBar, slideTime) {
     var stayTime= 10; //for now, can make each timeline as long as you want later
        
     var tlCommercial = new TimelineLite({
      onUpdate:setProgress, 
      onUpdateParams:["{self}", progressBar]
     });
        
     
     tlCommercial.set(imgSlides[2], {display: "block"})
     .to(progressBar, slideTime, {autoAlpha: 1}, 0)
     .to(imgSlides[2], slideTime, {autoAlpha:1}, 0)
     .to(imgSlides[2], slideTime, {autoAlpha:0}, stayTime)
     .to(progressBar, slideTime, {autoAlpha:0}, stayTime)
     .set(imgSlides[2], {display: "none"}, onComplete: updateCurrentIndex(0));
     
     return tlCommercial;
    }
    
    function setProgress (timeline, progressBar){
     TweenMax.set(progressBar, {scaleX:timeline.progress()});
    }
   }
  }
 });
<div ng-app="appBanner" ng-controller="bannerCtrl">
 <img class="imgSlide" src="images/slideshow/slideshow-1.jpg" >
 <img class="imgSlide" src="images/slideshow/slideshow-2.jpg" >
 <img class="imgSlide" src="images/slideshow/slideshow-3.jpg" >
 <div class="progress"></div>
 <div id="navArrowLeft" class="navArrow bannerNav" ng-click="prevSlide()">
  <div class="hidden-xs">
   <i class="fa fa-angle-double-left fa-5x"></i>
  </div>
  <div class="hidden-sm hidden-md hidden-lg">
   <i class="fa fa-angle-double-left fa-2x"></i>
  </div>
 </div>
 <div id="navArrowRight" class="navArrow bannerNav" ng-click="nextSlide()">
  <div class="hidden-xs">
   <i class="fa fa-angle-double-right fa-5x"></i>
  </div>
  <div class="hidden-sm hidden-md hidden-lg">
   <i class="fa fa-angle-double-right fa-2x"></i>
  </div>
 </div>
</div>

I have a controller which receives input from the user: next, previous, etc. based on the currentIndex. I have a javascript timeline that runs in the directive that changes the currentIndex as it plays. At the end of each of the nested child timelines, updateCurrentIndex is fired. It is necessary for me to communicate this change back to the controller so that the next, previous works on a meaningful currentIndex, not just what it was initialized in the beginning. The watch within my directive is registering the currentIndex = 0 initialization from my controller. So, I know that part of thecommunication works; its from directive to controller that is the problem.

My directive has its scope set to true and restrict is 'E' , how do I get these currentIndex changes back to controller so currentIndex can be incremented and decremented appropriately based on the user input? I know my way around the chrome debugger fairly well but can't find currentIndex in the scope within my directive . Was thinking that using the correct relational scope to the controller along with apply would be the answer, but I am stuck.

John Stafford
  • 565
  • 2
  • 9
  • 29
  • Also, if you are not famiar with Greensock, please don't get focused too much on that. onComplete is a utility method that runs at the end of a timeline. In this case, it is updating the currentIndex to the next slide. This value then needs to sync back with the controller. – John Stafford Mar 24 '16 at 20:02

1 Answers1

1

Angular does not know when changes are made by GreenSock, so your scope hasn't been updated. You can do this by using something like $scope.$apply() or $timeout(). You might get digest errors using $apply, so I'd go with using $timeout in your onComplete callback.

function onComplete() {
  $timeout(function() {
    updateCurrentIndex(1);
  });
}

Here's a demo that shows what happen when you don't use $apply or $timeout with a third-party library... nothing! And yes, that includes jQuery too.

http://plnkr.co/edit/Z28hiklSk8IsyQ2Bi45s?p=preview

Blake Bowen
  • 1,049
  • 1
  • 12
  • 36
  • Thanks Blake! Never see $timeout before. – John Stafford Mar 24 '16 at 22:07
  • 1
    You may find this article about $timeour of interest: [Why would I wrap a function in AngularJS $timeout service . . .](http://stackoverflow.com/questions/36108158/why-would-i-wrap-a-function-in-angularjs-timeout-service-without-a-delay-like-t/36108732#36108732) – Thomas Maddocks Mar 25 '16 at 02:39