Angus Simons
Angus Simons

Reputation: 527

AngularJS - share variable between two controllers

I have two controllers that have to communicate each other. The first reference to a video player and the second one to a timeline.

From the first one, I get the currentTime of the video playback and I want to pass it to the second one that should move the time-bar as the video is playing.

I tried using the factory to share a variable called time between controllers but this doesn't change during the time.

First Controller:

angular.module('videoCtrl', ['vjs.video'])
  .controller('videoController', ['$scope', 'Timeline', function (scope, Timeline) {
        scope.mediaToggle = {
            sources: [
                {
                    src: 'http://static.videogular.com/assets/videos/videogular.mp4',
                    type: 'video/mp4'
                }
            ],
        };

        //listen for when the vjs-media object changes
        scope.$on('vjsVideoReady', function (e, videoData) {
          videoData.player.on('timeupdate', function () {
            var time = this.currentTime();
            Timeline.setTime(time); // setting the time on factory
          })
        });
    }]);

Second Controller:

angular.module('timelineCtrl', ['mt.media-timeline'])
    .controller('timelineController', function ($scope, Timeline) {
    $scope.time = Timeline.getTime(); // here I'm trying to get the time
  });

Factory:

.factory('Timeline', function(){
    var timelines = [];
    var time = null;
    return {

      getTime: function() {
        return time;
      },

      setTime: function(_time) {
        time = _time;
      }

    }
  });

Upvotes: 3

Views: 713

Answers (3)

Riiverside
Riiverside

Reputation: 798

Well as far as I can tell what're doing in the second controller is that you retrieve the value of time on instantiation of the controller. Of course further changes of the value in the service can't be picked up this way. To do that can use $scope.$watch in the second controller:

angular.module('timelineCtrl', ['mt.media-timeline'])
    .controller('timelineController', function ($scope, Timeline) {
    $scope.time = Timeline.getTime(); //Set the time once so it's not undefined

    $scope.$watch(
      function() {return Timeline.getTime();},
      function(newVal) {$scope.time = newVal;}
    );
});

Angular will call the first function in every $digest cycle(That's about at least every 10ms if I recall correctly) and will call the second function when a change has been detected. Detailed documentation for $watch can be found here

This is one way to do it. You could also add a function to your $scope(e.g. getTime()), which should return the current time, and then call this function in the HTML template: {{getTime()}}. Both ways pretty much work the same way, except that the second one leaves the 'dirty' work to angular(creating watchers and updating values)

Upvotes: 0

Claies
Claies

Reputation: 22323

time appears to be a primitive, which means it is returned byVal rather than byRef. In other words, each call to getTime will return the value that time is currently set to, and calls to setTime will change the value for future calls, but not for anything that already called it. This is a classic case of the angular rule, Always use a dot.

Try changing time to an object instead:

.factory('Timeline', function() {
  var timelines = [];
  var time = {
    value: null
  };
  return {

    getTime: function() {
      return time;
    },

    setTime: function(_time) {
      time.value = _time;
    }

  }
});

In your HTML, use {{time.value}}.

Upvotes: 5

FotisK
FotisK

Reputation: 157

Saving in $rootScope instead of $scope would give you the ability to access a variable across all your app and your controllers. But have in mind that creating a large number of $rootScope could affect your app's performance.

Do not forget to inject $rootScope into the controller (like you did with $scope), so you can access it.

Upvotes: 0

Related Questions