Angular-检测范围的变化

时间:2014-06-26 14:18:19

标签: javascript json angularjs d3.js angularjs-scope

我有一个服务,它为我抓取JSON数据并将其交给控制器:

服务代码段:

...
getP2PKeywordData: function(global_m, global_y) {

      // Return the promise
      return $http({
                url: base_url + 'get/P2P/kwords/', 
                method: "GET",

                // Set the proper parameters
                params: { 
                  year: global_y,
                  month: global_m
                  }
              })
              .then(function(result) {

                  // Resolve the promise as the data
                  return result.data;
              },
              function(data) {
                  // Error handling
              });
    }
    ...

控制器成功抓取数据,我已使用console.log行下方的$scope.d3Data = data;对其进行了测试。

Controller片段:

myApp.controller('DownloadsCloudCtrl', ['$scope', 
                                        '$rootScope', 
                                        'requestService',
                                        '$cookieStore',
  function($scope, $rootScope, requestService, $cookieStore) {
  $rootScope.$on('updateDashboard', function(event, month, year) {
    updateDashboard(month, year);
  });

  var updateDashboard = function(month, year) {
    requestService.getP2PKeywordData(month, year).then(function(data) {
      $scope.d3Data = data;
    });
  };

  updateDashboard($cookieStore.get('month'), $cookieStore.get('year'));
}]);

控制器连接到d3-cloud指令(d3字云),该指令实际上附加了正确的svg元素,并用数据绘制了单词云。 但是,出于某种原因,上述控制器未将$scope.d3Data传递给指令。

这很令人困惑,因为当我将数据数组硬编码到控制器中时,就像这样......

$scope.d3Data = [
    {
        'kword': 'a',
        'count': 20,
    },{
        'kword': 'b',
        'count': 10,
    ...

...它完美地连接到指令!

指令片段:

myApp.directive('d3Cloud', ['$window', 
                            'd3Service', 
                            'd3Cloud', 
                            function($window, 
                                     d3Service, 
                                     d3Cloud) {
  return {
    restrict: 'EA',
    scope: {
      data: '=',
      label: '@'
    },
    link: function(scope, element, attrs) {
      d3Service.d3().then(function(d3) {
        window.onresize = function() {
          scope.$apply();
        };
        scope.$watch(function() {
          return angular.element($window)[0].innerWidth;
        }, function() {
          scope.render(scope.data);
        });
        scope.render = function(data) {

HTML片段:

<div class="col-md-6">
    <div class="module">
      <div class="inner-module" ng-controller="DownloadsCloudCtrl">
        <div class="module-graph">
          <d3-cloud data="d3Data"></d3-cloud>
        </div>
      </div>
    </div>
  </div>

我尝试了什么:

  1. 我尝试在控制器中的$scope.$apply()行之后添加手册$scope.d3Data = data;。奇怪的是,这是第一次我这样做了,但是在每次页面刷新之后我都有一个“$digest已经在进行中”的错误(这是预期的......)。
  2. 为了解决$digest错误,我尝试将$apply函数封装在$timeout代码块中,甚至是可怕的$$phase条件。这两个解决方案都修复了控制台错误,但未能解决将数据从控制器传递到指令的原始问题。
  3. TL; DR:我很丢失。关于下一步排查问题的想法?

1 个答案:

答案 0 :(得分:1)

您似乎将响应视为承诺两次。所以一旦进入服务:

  .then(function(result) {

      // Resolve the promise as the data
      return result.data;
  },

在控制器中,您再次解决了承诺:

requestService.getP2PKeywordData(month, year).then(function(data) {
  $scope.d3Data = data;
});

这可以起作用,因为(根据我的理解)Angular有时会在绑定到范围时自动解析promise。

最好只在控制器中处理承诺。所以服务变成:

getP2PKeywordData: function(global_m, global_y) {

      // Return the promise
      return $http({
                url: base_url + 'get/P2P/kwords/', 
                method: "GET",

                // Set the proper parameters
                params: { 
                  year: global_y,
                  month: global_m
                  }
              });
    }

更新:

尝试将d3Data范围属性初始化为空集合,然后将响应数据推入其中。例如:

myApp.controller('DownloadsCloudCtrl', ['$scope', 
                                        '$rootScope', 
                                        'requestService',
                                        '$cookieStore',
  function($scope, $rootScope, requestService, $cookieStore) {

  //added
  $scope.d3Data = [];      

  $rootScope.$on('updateDashboard', function(event, month, year) {
    updateDashboard(month, year);
  });

  var updateDashboard = function(month, year) {
    requestService.getP2PKeywordData(month, year).then(function(data) {

         //then
         angular.forEach(data, function(thing) {

             $scope.d3Data.push(thing);
         )};

    });
  };

  updateDashboard($cookieStore.get('month'), $cookieStore.get('year'));
}]);