【问题标题】:AngularJS two controllers with shared model, controller 2 not seeing change to modelAngularJS 两个具有共享模型的控制器,控制器 2 没有看到模型的变化
【发布时间】:2015-10-14 10:56:10
【问题描述】:

达到了我的 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() 方法更新所有控制器。
  • 当我尝试像在$rootScope.$broadcast('scanner-started'); 中那样使用$rootScope 时,我得到相同的Uncaught ReferenceError: $rootScope is not defined 错误。我正在使用 Angular 1.4,我是否必须做一些特别的事情才能获得根范围,我似乎永远无法访问它。
  • 是的,您需要将$rootScope 注入到您的服务定义中。我将此代码添加到下面的答案中。

标签: javascript angularjs models controllers


【解决方案1】:

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

您的问题是$scope.chapters = newValue;,这就是您通过引入与您的服务无关的新对象来破坏控制器使用的绑定的地方。

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

为此,我使用以下方法:

    transferDataList = function (from, to) {
        /*
            http://stackoverflow.com/questions/1232040/empty-an-array-in-javascript
        */
        to.length = 0;
        for (var i = 0; i < from.length; i++) { to.push(from[i]); }
    };

    transferDataMap = function (from, to) {
        /*
            http://stackoverflow.com/questions/684575/how-to-quickly-clear-a-javascript-object
        */
        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 接口将始终更新。

【讨论】:

  • 嗨,我已经厌倦了这种方法,你是对的,我应该为我的模型使用 getter 和 setter,但这并没有解决问题,如果我将数据放入模板中,它不会当模型静止时更新。我用$scope.chapters = VideoPlayerModel.getChapterData( ); 链接到我的控制器,但是当我设置数据时,我没有像你一样得到更新can see here
【解决方案2】:

您可以使用$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.test = "Hello World"; $scope.$on('modelUpdated', function () { chapterListCtrl.setChapters(); $scope.test = chapterListCtrl.chapters.chapterPos; console.log($scope.test); }); 我的模板现在是这样的:&lt;p&gt;{{test}}&lt;/p&gt; - 它确实更改为 0,但随后保持为 0,但 console.log 通过 1、2、3 等正确增加
  • 如果我使用:$scope.$apply( function ( ) { $scope. test = chapterListCtrl.chapters.chapterPos; });,它可以工作,但我觉得我不应该这样做,我在初始化时收到错误:[$rootScope:inprog] 消息。
  • @KevinMann,你是对的。不要在您的角度代码中使用$scope.$apply()。它在你的 Angular JS 之外使用。因为,默认情况下,您的 Angular 代码将在每个摘要中调用 $scope.$apply
  • @KevinMann,在你的监听器函数中,你想做什么?实际上,在您的侦听器函数中,您需要从共享服务中获取模型并将其更新为您的控制器模型。
  • @KevinMann,我认为您现在可以从自己的控制器本身获得价值。但是你必须从你的服务中更新你的模型,$scope.test = YourService.GetValue()
【解决方案3】:

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

$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);

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

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

【讨论】:

    猜你喜欢
    • 2015-08-02
    • 1970-01-01
    • 2015-09-30
    • 1970-01-01
    • 2011-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-16
    相关资源
    最近更新 更多