【问题标题】:AngularJS - share variable between two controllersAngularJS - 在两个控制器之间共享变量
【发布时间】:2017-04-29 15:32:47
【问题描述】:

我有两个必须相互通信的控制器。 第一个引用视频播放器,第二个引用时间轴。

从第一个,我得到了视频播放的currentTime,我想将它传递给第二个,它应该在视频播放时移动时间栏。

我尝试使用工厂在控制器之间共享一个名为 time 的变量,但这在此期间不会改变。

第一控制器

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
          })
        });
    }]);

第二控制器

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

工厂

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

      getTime: function() {
        return time;
      },

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

    }
  });

【问题讨论】:

    标签: angularjs


    【解决方案1】:

    time 似乎是一个原语,这意味着它返回的是 byVal 而不是 byRef。换句话说,对getTime 的每次调用都将返回time 当前设置的值,对setTime 的调用将更改未来调用的值,但不会更改任何已经调用它的值。这是角度规则的经典案例,始终使用点

    尝试将time 改为一个对象:

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

    在您的 HTML 中,使用 {{time.value}}

    【讨论】:

    • 但这不会在播放期间更新另一个控制器中的时间变量。我已经能够在页面加载时获得time。我在第二个控制器中需要的 HTML 中不需要 time var。例如,如果我在 setTime(_time) 函数中写入 console.log(_time),我会在视频播放时获得 currentTime 的值变化
    • 这是console.log(_time)的结果 [Log] 0 (script.js, line 35222) [Log] 0.125696053 (script.js, line 35222) [Log] 0.377129754 (script.js, line 35222) [日志] 0.626557536 (script.js, 第 35222 行) [日志] 0.87789302 (script.js, 第 35222 行) [日志] 1.052918579 (script.js, 第 35222 行)
    • 如果videoData.player.on('timeupdate',... 正在触发,那么该对象应该不断接收新的_time,并将其保存到time.value。在 或者 另一个控制器或其关联的 HTML 中,time.value 应该具有 videoData 调用发布的最后一个值。
    • 但如果我将console.log() 放在第二个控制器中,我不会得到相同的结果。
    • 您希望发生什么?控制器是一个构造函数,它只触发一次,因此在控制器中放置console.log() 只会在控制器初始化时输出一个值一次。如果您想在每次time.value 更改时执行某项操作,那么您需要$watch
    【解决方案2】:

    保存在$rootScope 而不是$scope 将使您能够跨所有应用程序和控制器访问变量。但请记住,创建大量 $rootScope 可能会影响您的应用程序的性能。

    不要忘记将$rootScope 注入控制器(就像您对$scope 所做的那样),以便您可以访问它。

    【讨论】:

    • 这个完全不推荐,你可能会不小心覆盖$rootScope的值并添加不必要的$watchs
    • 使用$rootScope 在几乎所有情况下都是一种反模式。这是一个可以通过重组工厂来解决的案例。
    • 同意。 @AlonEitan。
    【解决方案3】:

    据我所知,在第二个控制器中所做的是您检索控制器实例化的时间值。当然,不能以这种方式获取服务中值的进一步变化。为此可以在第二个控制器中使用$scope.$watch

    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 将在每个$digest 循环中调用第一个函数(如果我没记错的话,至少大约每 10 毫秒一次)并在检测到更改时调用第二个函数。 $watch的详细文档可以找到here

    这是一种方法。您还可以向您的$scope(例如getTime())添加一个函数,该函数应返回当前时间,然后在HTML 模板中调用此函数:{{getTime()}}。两种方式的工作方式几乎相同,除了第二种方式将“脏”工作留给 angular(创建观察者和更新值)

    【讨论】: