简体   繁体   English

AngularJS数据绑定-监视周期未触发

[英]AngularJS Data Binding - Watch cycle not triggering

Assuming the following JSFiddle: https://jsfiddle.net/pmgq00fm/1/ 假设以下JSFiddle: https ://jsfiddle.net/pmgq00fm/1/

I want my NVD3 chart to update in real time, based on the setInterval() on line 39 that updates the data the directive is bound to. 我希望基于第39行上的setInterval()来实时更新NVD3图表,该setInterval()更新指令所绑定的数据。 Here are quick pointers on the architecture and code: 以下是有关体系结构和代码的快速指导:

  • The directive is within the scope of the ThisController 该指令在ThisController的范围内
  • The data is bound bi-directionally using the '=' in the directive 使用指令中的'='双向绑定数据
  • An update function is supposed to be called on the chart whenever the directive detects a change in the data during the $watch cycle. 只要指令在$ watch周期内检测到数据更改,就应该在图表上调用更新函数。
  • The $watch is a deep watch, so changes in values should be detected $ watch是一个很深的手表,因此应该检测值的变化
  • An interval was set in the directive to print in the console the changes in value. 在指令中设置了一个间隔,以在控制台中打印值的更改。 The controller prints the updated array whenever the updateFnc is called. 每当调用updateFnc时,控制器都会打印更新后的数组。
  • The values in the data in the directive never matches the data in the controller for some reason I ignore. 由于某些原因,指令中的数据值永远不会与控制器中的数据匹配。
  • The values are modified both in the controller and in the directive. 在控制器和指令中都可以修改这些值。
  • The $watch in the directive is only called on execution 指令中的$ watch仅在执行时调用
  • The code is based on this tutorial and respects all of the steps necessary for data binding explained in it. 该代码基于本教程,并遵循其中说明的数据绑定所必需的所有步骤。

To debug, run the JSFiddle and open the console to see the objects and debug prints. 要调试,请运行JSFiddle并打开控制台以查看对象和调试打印。

HTML: HTML:

<div id="stats-container" ng-app="myApp" ng-controller="ThisController">
    <div id="nvd3-test" nvd3-discrete-bar data="data.graph">       
</div> 

JS: JS:

myControllers.controller('ThisController', ['$scope', function ThisController($scope){
      $scope.data = { graph : [ ... ] };

      updateFnc = function(data){
        for(var i = 0; i < data[0].values.length ; i++){
          data[0].values[i].value = Math.random();
        }
        console.log(Date.now()/1000 + ": In Controller");
        console.log(data);
      };

      setInterval(function(){
        updateFnc($scope.data.graph);
      }, 1000);
 }]);

myServices.factory('NVD3Wrapper', ['NVD3S', 'D3S', '$q', '$rootScope', function NVD3Wrapper(nvd3s, d3s, $q, $rootScope){
  return {
    nvd3: nvd3s,
    d3: d3s,
    discreteBarChart : function(data, config){
        var $nvd3 = this.nvd3,
            $d3 = this.d3,
            $nvd3w = this, //In order to resolve the nvd3w in other scopes.
            d = $q.defer(); //Creating a promise because chart rendering is asynchronous
        $nvd3.addGraph(function() {
          ...
        });
        return {
          chart: function() { return d.promise; } //returns the chart once rendered.
        };
    },
    _onRenderEnd: function(d, chart){
      $rootScope.$apply(function() { d.resolve(chart); });
    },
  };
}]);

myDirectives.directive('nvd3DiscreteBar', ['NVD3Wrapper', function(nvd3w){
  return {
    restrict: 'EA',
    scope: {
      data: '=' // bi-directional data-binding
    },
    link: function(scope, element, attrs) {
      var chart,
          config = {
            target: element,
          };
      var wrapper = nvd3w.discreteBarChart(scope.data, config);
      wrapper.chart().then(function(chart){
        scope.$watch(function() { return scope.data; }, function(newValue, oldValue) {
          console.log(Date.now()/1000 + ": In Directive $watch");
          if (newValue)
              chart.update();
        }, true);
      });
      //For testing
      setInterval(function(){ console.log(Date.now()/1000 + ": In Directive"); console.log(scope.data); }, 1000);
    }
  };
}]);

Any help would be greatly appreciated! 任何帮助将不胜感激! Thanks a lot! 非常感谢!

EDIT : New JSFiddle with Andrew Shirley's answer: https://jsfiddle.net/pmgq00fm/3/ 编辑 :新的JSFiddle与Andrew Shirley的答案: https ://jsfiddle.net/pmgq00fm/3/

Add the line 添加行

$scope.$apply()

to the update function. 更新功能。 This is because when a variable is updated in the javascript, outside of the common angular functions, angular isn't aware of the change and therefore doesn't waste any effort refreshing the DOM. 这是因为,当在javascript中更新变量时,除了常见的角度函数之外,角度不了解更改,因此不会浪费任何精力来刷新DOM。 This forces a digest cycle which should refresh what you see. 这将强制执行摘要循环,从而刷新您所看到的内容。

EDIT: It is pretty important to actually understand why you're actually using a scope.apply and I don't feel I've described it incredibly well. 编辑:真正了解您为什么真正使用scope.apply是非常重要的,我不认为我对它的描述非常好。 Here's an article that does that much better than I have. 这是一篇比我做得更好的文章。

http://jimhoskins.com/2012/12/17/angularjs-and-apply.html http://jimhoskins.com/2012/12/17/angularjs-and-apply.html

I point this out because you need to be aware that if you ARE within a function that's closely tied to angular (eg something called by ng-click), then if you attempt to use a scope.$apply you will get javascript errors, because you'll already be in the middle of a digest cycle. 我指出这一点是因为您需要注意的是,如果您位于与角度紧密相关的函数中(例如ng-click调用的内容),那么如果您尝试使用范围。$ apply会导致JavaScript错误,因为您已经处于摘要周期的中间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM