AngularJS 两个控制器共享模型,控制器 2 没有看到模型变化

AngularJS two controllers with shared model, controller 2 not seeing change to model

触及我的 Angular 知识的上限,我一直在兜圈子。

基本上我有视频播放器和章节列表指令,每个指令都有一个控制器。控制器使用相同的模型服务,如下所示:

  .service('VideoPlayerModel', function(){
      var model = this;
      model.chapters = {
          chapterPos: 0,
          targetChapter:null,
          data: []
      },
      model.getVideoData = function() {
          return model.videoData;
      };
      model.setVideoData = function(vData){
        ...
        ...
        ...
      };
  });

在视频播放器控制器中,当播放器更新时,它会找到所需的章节数据并像这样更新 model.chapters 数据:

   updateChapter: function(currentTime){
    var chapters = VideoPlayerModel.chapters;
    var chaptersCtrl = videoPlayerCtrl.chapters;

    if (chapters.nextChapter.start <= currentTime) {
      chapters.chapterPos = chapters.chapterPos + 1;
      chaptersCtrl.setChapter(); //This finds and sets the Target Chapter
    }
  },

setChapter 运行后,我调用 console.log(VideoPlayerModel.chapters),我可以看到数据模型已更新,结果如下:

Object {chapterPos: 1, targetChapter: Object, data: Array[6], nextChapter: Object}

然而,ChapterListCtrl 中的手表没有触发,任何显示 ChapterPos 的屏幕项目仍然只显示初始值 0。

控制器看起来像这样:

.controller("ChapterListCtrl", ['$scope', 'VideoPlayerModel', function($scope, VideoPlayerModel) {
  $scope.chapters = VideoPlayerModel.chapters;

  $scope.$watch(function() { return VideoPlayerModel.chapters; }, function(newValue, oldValue){ 
    $scope.chapters = newValue; 
    console.log("A Change"); // Only runs at initialisation.
  });
}])

我尝试了不同的方法并最终得到了这个,不确定我现在的方向是否完全错误。有人可以帮忙吗?

您可以使用 $broadcast()$on() 函数来实现您的要求。

$broadcast() 会将事件刷新到它的所有子控制器。因此,当您为共享模型设置新值时,您可以 $broadcast() 向所有控制器发送具有新值的事件。

在您的共享服务中添加广播方法。

model.setVideoData = function(vData){
    UpdateYourModel();
    // Inform that your model is updated 
    $rootScope.$broadcast('modelUpdated');
}

现在您可以在所有控制器中为事件 modelUpdated 添加侦听器。

$scope.$on('modelUpdated', function () {
     $scope.controllerModel = VideoPlayerModel.getVideoData(); // Update your controller model
}

此外,将 $rootScope 注入您的服务,

.service("VideoPlayerModel", ["$rootScope", function($rootScope){
     // define your service here.
}] );

就这些了!!!

希望对您有所帮助。

尝试将您的观察者更改为:

$scope.$watch('chapters', function(newValue, oldValue){ 
    $scope.chapters = newValue; 
    console.log("A Change"); // Only runs at initialisation.
});

或者,如果这没有达到您想要的效果,您可以通过传递第三个参数来启用深度监视:

$scope.$watch('chapters', function(newValue, oldValue){ 
    $scope.chapters = newValue; 
    console.log("A Change"); // Only runs at initialisation.
}, true);

你的观察者不会触发,因为它总是 returns 与 chapters 相同,Angular 认为它没有改变,因为它通过引用进行检查。您的观察者也可以重构为:

  $scope.$watch(function() { return VideoPlayerModel.chapters.length; }, function(newValue, oldValue){ 
    $scope.chapters = newValue; 
    console.log("A Change"); // Only runs at initialisation.
  });

您不需要使用 $watch$broadcast$on。这最好通过常规 JavaScript 思考来解决。

你的问题是 $scope.chapters = newValue; 那是你通过引入一个与你的服务无关的新对象来打破你的控制器使用的绑定。

您应该做的是考虑您的服务 model.chapters = {..} 并说嘿!这是我将使用的唯一对象。如果我想在任何地方更改此对象中的数据,我将切换对象内部的数据,而不是将新对象分配给我使用的引用。

为此,我使用以下方法:

    transferDataList = function (from, to) {
        /*
            
        */
        to.length = 0;
        for (var i = 0; i < from.length; i++) { to.push(from[i]); }
    };

    transferDataMap = function (from, to) {
        /*
            
        */
        var member;
        for (member in to) { delete to[member]; }
        for (member in from) { to[member] = from[member]; }
    };

当我想更改对象中的数据时,我不会这样做:

$scope.chapters = newValue;

相反,我这样做:

transferDataMap(newValue, $scope.chapters);

或:

transferDataList(newValue, $scope.chapters);

这样您将保持绑定,并且您的 Angular 界面将始终更新。