5

Whenever I click on one of the points it causes the chart to update. This only happens if I've got an ngClick directive on the <nvd3> element (even if the listener function does nothing). If I remove the ngClick, everything's fine.

I'm using a pretty basic nvd3 scatter chart with angular-nvd3.

What could be causing this strange interaction?

(The framerate of this recording is low, so it's hard to see but the chart is redrawn on each click, including the top one)

Clicking points on a graph

Here's a plunker that reproduces the issue:

http://plnkr.co/edit/F0ZslBaisoHWIp0VcI8o

Thanks!

UPDATE I've narrowed it down to a watch within angular-nvd3. Somehow the presence of the ngClick is causing the 'data' to change. This line is triggering the refresh: https://github.com/krispo/angular-nvd3/blob/master/dist/angular-nvd3.js#L328 There's nothing in my code that changes 'data' (also verified the object is the same instance outside the directive), and there's nothing I can see in angular-nvd3.js that could change the data at all... hmmm...

UPDATE Here's my click event listener (it's empty):

controller.handleChartClick = function(event) {
};

and html:

<div class="col-sm-10">
    <nvd3 ng-click="observationsCharts.handleChartClick($event)"
              options="observationsCharts.scatterPlotChartOptions"
              data="observationsCharts.scatterPlotChartData"></nvd3>
</div>
HankScorpio
  • 3,612
  • 15
  • 27
  • what are you calling on ng-click? – terafor Aug 25 '15 at 06:25
  • Is your data built exactly the same as in the plunkr? There must be something different within your code if the issue doesn't reproduce in the example. Can you post your actual code? – Stacey Burns Aug 25 '15 at 14:19
  • @StaceyBurns, I updated my question and the plunker, please take a look. Now it reproduces the issue and it also has some of the data I'm using in my production code. The other differences are: I updated the versions of d3, nvd3, and angular-nvd3 in the plunker (the plunker was using old versions and I didn't notice) and I also stripped it down to the minimum code. – HankScorpio Aug 25 '15 at 17:54
  • @Terafor an empty click handler. Check out the updated question. – HankScorpio Aug 25 '15 at 17:54
  • this is wired. i guess workaround is to make an absolutely positioned element and use ng-click on that... – terafor Aug 26 '15 at 07:30
  • You mean something to overlay on top of the chart? The point of clicking on the chart is to get the data about the point I click on, using `event.target`. I wouldn't be able to do that with an overlay. – HankScorpio Aug 26 '15 at 17:02

2 Answers2

2

You are correct that the following watch is causing the issue:

// Watching on data changing
 scope.$watch('data', function (newData, oldData) {
  if (newData !== oldData && scope.chart) {

    if (!scope._config.disabled && scope._config.autorefresh) {
        scope._config.refreshDataOnly && scope.chart.update ? scope.chart.update() : scope.api.refresh(); // if wanted to refresh data only, use chart.update method, otherwise use full refresh.
                            }
                        }
                    }, scope._config.deepWatchData);

The thing is, if you add a log in this function to see what the old and new data is, the data is actually changing.

When you click on one of the points, nvd3 is adding a color attribute to the data.

old data:

[{"key":"Battery Voltage","values":[{"x":1439419440000,"y":90,"series":0},{"x":1439419440000,"y":43,"series":0},{"x":1439419440000,"y":345,"series":0},{"x":1439167620000,"y":73,"series":0},{"x":1439167620000,"y":42,"series":0},{"x":1439167620000,"y":36,"series":0},{"x":1440010740000,"y":32,"series":0},{"x":1439419440000,"y":62,"series":0},{"x":1439167620000,"y":73,"series":0},{"x":1439167620000,"y":42,"series":0},{"x":1439167620000,"y":36,"series":0}]}] 

new data:

[{"key":"Battery Voltage","values":[{"x":1439419440000,"y":90,"series":0,"color":"#1f77b4"},{"x":1439419440000,"y":43,"series":0,"color":"#1f77b4"},{"x":1439419440000,"y":345,"series":0},{"x":1439167620000,"y":73,"series":0},{"x":1439167620000,"y":42,"series":0},{"x":1439167620000,"y":36,"series":0},{"x":1440010740000,"y":32,"series":0},{"x":1439419440000,"y":62,"series":0,"color":"#1f77b4"},{"x":1439167620000,"y":73,"series":0},{"x":1439167620000,"y":42,"series":0},{"x":1439167620000,"y":36,"series":0}],"color":"#1f77b4","value":90}] 

Each time you click a new point, the color attribute is added to the object, so the watch function is actually running correctly.

I would advise opening an issue over at https://github.com/krispo/angular-nvd3

**Edit - Just to clarify, the reason it happens when ng-click is added is because this forces the Angular digest cycle to run which will trigger the watch.

Stacey Burns
  • 1,092
  • 7
  • 14
  • Ok, thanks. Looks like I overlooked the part about `scope._config.deepWatchData`. Setting that to false takes care of the problem... although it does still feel like less than ideal to need to do that. – HankScorpio Aug 26 '15 at 20:51
  • Yep it doesn't seem the most ideal and this will only work if your data doesn't change. For example, if you set DeepWatch to false and add this to your ng-click : $scope.data[0].values[0].y = 300 , it won't update the chart. (It updates the chart when its set to true). – Stacey Burns Aug 27 '15 at 08:59
2

Looks like the only workaround to this (for now) is to use the config setting deepWatchData: false. That prevents the $watch getting triggered too often.

<nvd3 config="{deepWatchData: false}" options="..." data="..."/>
HankScorpio
  • 3,612
  • 15
  • 27