Search code examples
d3.jsdc.jslinechartcrossfilterrangeslider

dc.js - Remove the old chart and redraw chart on range slider filter


I am trying to use a range slider to filter line charts. I took the adjustable-threshold example as a reference. Now, the slider is filtering the data, but each time the slider is moved, new chart keeps getting added but the old chart does not disappear.

This is my code:

      var ndx = crossfilter(data);

      var dummy = ndx.dimension(function(d) {
        return [+d.xAxis, d.some_no];
      });

      var speed = ndx.dimension(function(d) {
        return +d.xAxis;
      });
      var speedGrp = speed.group().reduce(
        function(p, v) {
          p.yOne = p.yOne + +v.yOne;
          p.yTwo = p.yTwo + +v.yTwo;
          return p;
        },
        function(p, v) {
          p.yOne = p.yOne - +v.yOne;
          p.yTwo = p.yTwo - +v.yTwo;
          return p;
        },
        function() {
          return {
            yOne: 0,
            yTwo: 0
          };
        });

      function coreCount_from_threshold() {
        var scoreThreshold = document.getElementById('slideRange').value;
        scoreThreshold = parseFloat(scoreThreshold);
        if (isNaN(scoreThreshold)) {
          scoreThreshold = 10
        }
        return ndx.dimension(function(d) {
          var maxNumber = 16;
          if ((+d.xAxis > scoreThreshold) && (+d.xAxis < maxNumber)) {
            return +d.xAxis;
          } else {
            return null;
          }
        });
      }
      var coreCount = coreCount_from_threshold();
      var coreCountGroup = coreCount.group().reduce(
        function(p, v) {
          p.yOne = p.yOne + +v.yOne;
          p.yTwo = p.yTwo + +v.yTwo;
          return p;
        },
        function(p, v) {
          p.yOne = p.yOne - +v.yOne;
          p.yTwo = p.yTwo - +v.yTwo;
          return p;
        },
        function() {
          return {
            yOne: 0,
            yTwo: 0
          };
        });



      chart
        .width(500)
        .height(300)
        .margins({
          top: 20,
          left: 40,
          right: 20,
          bottom: 60
        })
        .x(d3.scaleLinear().domain([0, 17]))
        //.legend(dc.legend().x(60).y(20).itemHeight(13).gap(5))
        .renderHorizontalGridLines(true)
        .elasticX(true)
        .elasticY(true)
        .compose([
          dc.lineChart(chart)
          .dimension(coreCount)
          .colors('green')
          .group(coreCountGroup)
          .valueAccessor(function(d) {
            return d.value.yOne;
          })
          .curve(d3.curveLinear)
          .dashStyle([5, 5]),
          dc.lineChart(chart)
          .dimension(coreCount)
          .colors('#FA8072')
          .group(coreCountGroup)
          .valueAccessor(function(d) {
            return d.value.yTwo;
          })
          .renderArea(true)
          .curve(d3.curveLinear)

          // .useRightYAxis(true)
        ])
        .brushOn(false);

      dc.renderAll();
      $('#slideRange').change(function(slideValue) {
        var sliderDiv = document.getElementById("sliderValue");
        sliderDiv.innerHTML = slideValue;
        console.log(sliderDiv.innerHTML);
        coreCount.dispose();
        // coreCountGroup.dispose();
        coreCount = coreCount_from_threshold();
        coreCountGroup = coreCount.group().reduce(
          function(p, v) {
            p.yOne = p.yOne + +v.yOne;
            p.yTwo = p.yTwo + +v.yTwo;
            return p;
          },
          function(p, v) {
            p.yOne = p.yOne - +v.yOne;
            p.yTwo = p.yTwo - +v.yTwo;
            return p;
          },
          function() {
            return {
              yOne: 0,
              yTwo: 0
            };
          });
        chart
          .dimension(coreCount)
          .group(coreCountGroup)
          .compose([
            dc.lineChart(chart)
            .dimension(coreCount)
            .group(coreCountGroup)
            .valueAccessor(function(d) {
              return d.value.yOne;
            }),
            dc.lineChart(chart)
            .dimension(coreCount)
            .group(coreCountGroup)
            .valueAccessor(function(d) {
              return d.value.yTwo;
            })

          ]);
        dc.redrawAll();
      });

    });

Here is a fiddle for the same. How do I fix this?

My plan is to have multiple sliders as filters for multiple charts.

I have seen an example with use of two sliders. It uses d3.slider.js. But I havent found a compatible version of this library for DC v3.0.6 and D3 v5.

Advice on any other approach to implement multiple sliders as filters for multiple charts is welcome.

Thanks in advance.


Solution

  • The compositeChart doesn't have good ways to remove or replace child charts. It looks like what is happening in your fiddle is that it adds the new child charts but loses track of the old ones.

    Instead of trying to replace them, I would suggest just feeding the new data to the existing child charts.

    First, keep a reference to each of the child charts:

          var oneLine, twoLine;
          chart
            // ...
            .compose([
              oneLine = dc.lineChart(chart)
              .dimension(coreCount)
              // ...
              .dashStyle([5, 5]),
              twoLine = dc.lineChart(chart)
              // ...
            ]);
    

    Then, each time the slider changes, replace the child chart dimension and group instead of calling compose each time:

            oneLine
              .dimension(coreCount)
              .group(coreCountGroup);
            twoLine
              .dimension(coreCount)
              .group(coreCountGroup);
    

    As a bonus, changing the data instead of replacing the chart causes the charts to animate from one data to the next.

    Fork of your fiddle.