在控制器之间共享数据并处理更新/更改通知

时间:2014-08-19 14:04:05

标签: angularjs angularjs-scope

我正试图在多个控制器之间分享数据,但却无法找到它应该如何工作(角度方式)。我创建了一个看起来像这样的数据服务:

angular.module('myapp.services')
  .service('DataSet', function($rootScope) {

  return {

    filter: function(filterMethod) {
      /// ... do async stuff
      $rootScope.$broadcast("Data::filtered");
    },

    brush: function(brushed) {
      /// ... do async stuff
      $rootScope.$broadcast("Data::brushed");
    },

    load: function() {
      /// ... do async stuff
      $rootScope.$broadcast("Data::loaded");
    }
  };
});

接下来我想重用和更新此服务中的数据,因此我在控制器中使用它,如下所示:

angular.module('myapp.controllers')
  .controller('FilterCtrl', function ($scope, $rootScope, DataSet) {

  $scope.safeApply = function(fn) {
    var phase = this.$root.$$phase;
    if(phase == '$apply' || phase == '$digest') {
      if(fn && (typeof(fn) === 'function')) {
        fn();
      }
    } else {
      this.$apply(fn);
    }
  };

  function updateBrushed() {
    $scope.safeApply(function() {
      $scope.brushed = DataSet.brushed;
    });
  };

  $scope.brushed = [];

  $scope.keepSelected = function() {
    DataSet.filter(DataSet.FilterMethod.KEEP);
  };

  $scope.removeSelected = function() {
    DataSet.filter(DataSet.FilterMethod.REMOVE);
  };

  $scope.$on('Data::brushed', updateBrushed);
  $scope.$on('Data::filtered', updateBrushed);
});

我遇到的问题基本上是通过使用saveApply调用来说明的。基本上我从这里得到了这段代码:https://coderwall.com/p/ngisma。但我不明白的是为什么我需要它。据我所知,在更新DataSet服务时,我在'$ angular'内。尽管如此,如果没有调用saveApply,Filter控制器的视图就不会更新($ apply根本不起作用,因为我遇到了申请已经在进行中的问题)。

所以,基本上我的问题归结为:上面的方法是一种分享数据的好方法,如果是这样的话,服务中的变化通知应该如何工作?

更新:根据Julian Hollman的建议,我提出了以下解决方案:http://jsfiddle.net/Ljfadvru/7/。这或多或少地说明了我正在处理的完整工作流程,尽管其中一些工作流程是在小提琴中自动引发的,而不是基于我的实际应用程序中的用户交互。我喜欢这种方法的方法是它只在所有数据更新时发送信号。

根据Ed Hinchliffe的建议,使用参考资料也很不错。但是,我正在开发一个Web可视化框架,我期待成千上万的项目。清除数组和推送新元素(在我看来这个提议的结果)实际上是不可行的(如果我很好地理解这个范例,它也会导致我对每次更改的重新呈现)。如果有进一步改进的建议,我会立即纠正。

2 个答案:

答案 0 :(得分:1)

$broadcast不会触发$apply而我打赌你的“异步内容”不是来自角度的$ http。 所以在角度和角度之外发生的事情并不知道某些事情发生了变化。

在我看来,在这种情况下最好的事情是为你的异步代码编写一个包装器,并在从后端返回日期之后触发$ apply。不要在控制器中这样做。

答案 1 :(得分:1)

说实话,我不确定您的特定场景中的摘要循环究竟发生了什么,但我不认为您正在以正确的方式接近这一点。

' angular'方式,就是使用承诺。

您的服务应该更像这样:

angular.module('myapp.services')
  .service('DataSet', function($rootScope) {
  return {
    filter: function(filterMethod) {
      var returnData = []
      $http.get('/some/stuff').then(function(data){
        for(i in data){
          returnData.push(data[i]);
        }
      });
      return returnData;
    }
  };
});

这会设置一个空的占位符对象(returnData),可以立即传递给控制器​​,但会保留一个引用,以便在数据返回时您可以追溯地填充该对象。由于控制器和服务引用相同的对象,因此只需要工作即可。

这样您就不必担心与$digest$apply$broadcast打交道。

您的控制器只需拨打$scope.filtered = DataSet.filter();

即可

修改

如果您希望能够从多个控制器访问完全相同的数据:

angular.module('myapp.services')
  .factory('DataSet', function($http) {

  var cache = {
    filtered: []
  }

  return {
    getFiltered: function(){
      if(cache.filtered.length) return cache.filtered;
      $http.get('/some/url/').then(function(data){
        for(i in data){
          cache.filtered.push(data[i]);
        }
      });
    }
  };
});