Search code examples
dc.jscrossfilter

Unfiltered data on/off


I am using composite charts to see the unfiltered data, but I want to hide sometimes the unfiltered data and make the 'y' axis elastic. Hiding the unfiltered data wasn't hard, just an event listener on chart, but I can't make possible the elasticity on 'y' axis, when the unfiltered data is hidden. Perhaps it's not even possible in a case like this. Any ideas?

      chart.select('.unfiltered_data').on('change', function() {
        if(!this.checked) {
          console.log("Stop showing unfiltered data!")
          chart.select('.sub._0')
            .attr('visibility', 'hidden')
          // chart.elasticY(true)
          chart.redraw()
        }
        else {
          console.log("Show unfiltered data!")
          chart.select('.sub._0')
            .attr('visibility', 'visible')
          // chart.elasticY(false)
          chart.redraw()
        }
      })

Solution

  • There is (almost) always a way to do it in dc.js, because dc.js is a leaky abstraction by design!

    First I tried to change which child charts are included in each composite chart, but that wasn't the right approach because a composite chart's children can't be changed on a redraw, only on a render. And we want to animate when switching between showing the unfiltered and not showing.

    So instead I thought we could

    • use your visibility idea
    • turn off elasticY when the unfiltered is hidden, and
    • use the filtered child chart's domain instead

    So I added a checkbox

      <label><input type="checkbox" id="unfiltered" name="unfiltered" checked>&nbsp;Show Unfiltered</label>
    

    and a global variable

      var show_unfiltered = true;
    

    The handler looks like this:

      function ydomain_from_child1(chart) {
          chart.y().domain([0, chart.children()[1].yAxisMax()]);
          chart.resizing(true);
      }
      d3.select('#unfiltered').on('change', function() {
          show_unfiltered = this.checked;
          charts.forEach(chart => {
              chart.select('.sub._0').attr('visibility', show_unfiltered ? 'visible' : 'hidden');
              chart.elasticY(show_unfiltered);
              if(!show_unfiltered) {
                  ydomain_from_child1(chart);
                  chart.children()[1].colors(d3.schemeCategory10);
                  chart.on('preRedraw.hide-unfiltered', ydomain_from_child1);
              }
              else {
                  chart.children()[1].colors('red');
                  chart.on('preRedraw.hide-unfiltered', null);
              }
          })
          dc.redrawAll();
      });
    

    Whenever the checkbox is toggled, we turn on or off elasticY based on the setting. When the unfiltered are not shown, we'll simulate elasticY with a preRedraw handler which determines the domain from the second (filtered) child chart.

    Additionally, we turn on/off the red color scheme for the filtered chart based on the checkbox.

    I have added this to the compare unfiltered example.

    I found I had to make one more change: the filtered chart was hidden when there were no filters. So I had to disable this hiding if the unfiltered was unchecked:

    var any_filters = !show_unfiltered || charts.some(chart => chart.filters().length);
    chart.select('.sub._1')
        .attr('visibility', any_filters ? 'visible' : 'hidden')