1

I have a factory for creating dc.js charts which uses crossfilters functions bound to $scope. Plnkr Example

$scope.updateMyPie = function(data) {
     var cf = crossfilter(data)
     $scope.mypie.dimension = cf.dimension(function(d) { return d.key })
     $scope.mypie.group = 
     $scope.mypie.dimension.group().reduceSum(function(d){ return d.value });
}

chart factory takes all chart properties through the DOM via angular directive so the tags are like so

<mypie id="mypie" chart-dimesion="mypie.dimension" chart-group="mypie.group"...etc

this approach works fine for filtering, initial data loading, binding and chart rendering, etc....however this is one exception. If I have something like a button click event that fires a new data request, and then I use the above as the callback it takes and processes the data fine, but doesn't update the chart. Even if I include dc.redrawAll() or renderAll(), etc.

I have tried embedding redraw(), redrawAll(), verified the data is coming back correctly, destroying the tag entirely and re-creating and appending to DOM,etc. My assumption is there is an issue with my usage of this factory-style implementation of creating the DC/D3 charts, but I would assume that if I updated the group and/or dimenstion in the scope it should still work? It seems as though the chart objects don't pick up the changes to the dim/groups? I can verify that the cf group values referenced by the $scope.etc have changed by using the following in both the initial call and second call:

var print_filter = function(filter) {
    var f = eval(filter);
    if (typeof (f.top) != "undefined") { 
        f = f.top(Infinity);
    }
    if (typeof (f.dimension) != "undefined") { 
        f = f.dimension(function (d) { 
            return ""; 
        }).top(Infinity);
    }

    console.log(filter + "(" + f.length + ") = " + JSON.stringify(f).replace("[", "[\n\t").replace(/}\,/g, "},\n\t").replace("]", "\n]"));
};

UPDATE: added plnkr, and attempt to better explain= so with the example I have sort of a "traditional" way to build the dc chart versus the service based approach I am trying to achieve using a directive and element. Everything works except what I have attempted to show in the plnkr...when I request new data (ajax) the first updates fine, with the directive based approach nothing seems to work. I have tried numerous ways but essentially updating the $scope dim/group should be working right? I believe it is two way binding therefore these "two scopes" should share a reference?? But even if I use scope.$parent.my.dimension, etc it doesn't affect it.

Justin
  • 4,461
  • 22
  • 87
  • 152

1 Answers1

1

Your chart is still bound to your old crossfilter, dimension, and groups. Generally, when you load new data or replace data, don't throw away your crossfilter, dimension, or groups. Use the crossfilter.remove and crossfilter.add methods to add or remove data.

Here's how to modify your example:

Create your Crossfilter, dimensions, and groups up front when you create your controller, then don't create or destroy any more of them in the future:

myapp.controller('mycontroller', ['$scope', 'dataCaller', function($scope, dataCaller){
      $scope.pageTitle = "Updating DC.js and Crossfilter";

      $scope.currentData1 = "data1.json";
      $scope.currentData2 = "data1.json";

      $scope.my = {};
      $scope.my.cf = crossfilter();
      $scope.my.dimension = $scope.my.cf.dimension(function(d){ return d.key; })
      $scope.my.group = $scope.my.dimension.group().reduceSum(function(d){ return d.value; })

Then change your update function to just switch out the data in the Crossfilter:

 $scope.factoryBuildPieExample = function(data) {
    $scope.my.cf.remove()
    $scope.my.cf.add(data.response.data)
    dc.redrawAll();
  }

If you want to maintain your dc.js filters when you do this, this question/answer explains how: Updating dc.js data and reapplying original filters

Here is a working Plunker: http://plnkr.co/edit/UbfKsuhg9vH678MR6fQT?p=preview

Community
  • 1
  • 1
Ethan Jewett
  • 6,002
  • 16
  • 25
  • Awesome! Thanks once again Ethan. This was the underlying to the filters question I asked about the scatter plot yesterday actually. Appreciate the help man! – Justin Apr 19 '17 at 15:26
  • Ethan, I have noticed after implementing this inside an update function, I am passing in the data, consoling it and everything is there, then cf.remove, then cf.add and I am only getting the first object of the response array added? Any ideas? – Justin May 04 '17 at 14:51
  • I should mention that I am calling it from renderlet click on the chart itself...so the selection on the chart is retained (ideally), and the callback includes parameters to mock the filtering. – Justin May 04 '17 at 15:19
  • The behavior you describe with `add` doesn't sound right, but I don't understand what you are doing around filters and such from the comment. I think you'll have to put together an example and open a new question for this one. – Ethan Jewett May 04 '17 at 15:22