Using turnOnControls() in a sibling div

228 views
Skip to first unread message

Edwin Shin

unread,
May 18, 2015, 5:45:20 AM5/18/15
to dc-js-us...@googlegroups.com
Hi,

I'm trying to use a "reset" link (as shown in the various chart examples on https://dc-js.github.io/dc.js/).

However, I have i) a separate header div which contains the title/description for the chart and ii) the chart div.

The reset link only works when placed in the chart div, but I'd like it to appear in the header div.

Here's a simplified example, adapted from the bar.html example:

  https://jsfiddle.net/9fhywdu8/1/

Can someone suggest how I could get the reset link working from the sibling header div?

(For context, I'm putting a bunch of dc.js charts in a Bootstrap/AdminLTE-based dashboard, and I'd like to use the given header and body div structure).

Thanks in advance!

Gordon Woodhull

unread,
May 21, 2015, 12:19:52 PM5/21/15
to dc.js user group
Hi Edwin,

I think you would have to override turnOnControls and turnOffControls to get this to work:

    _chart.turnOnControls = function () {
        if (_root) {
            _chart.selectAll('.reset').style('display', null);
            _chart.selectAll('.filter').text(_filterPrinter(_chart.filters())).style('display', null);
        }
        return _chart;
    };

    _chart.turnOffControls = function () {
        if (_root) {
            _chart.selectAll('.reset').style('display', 'none');
            _chart.selectAll('.filter').style('display', 'none').text(_chart.filter());
        }
        return _chart;
    };

They are explicitly selecting elements with the 'reset' and 'filter' class within the chart div. You could override them for the charts you're interested in, to select elements which are outside the chart... Override, as in, just assign some other functions to those members, or use dc.override if you want to reuse the current functionality within your functions.

Cheers,
Gordon


--
You received this message because you are subscribed to the Google Groups "dc-js user group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-gro...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dc-js-user-group/25e111b2-6ad9-4dd6-a83d-af0350e1544d%40googlegroups.com.

Gordon Woodhull

unread,
May 21, 2015, 12:31:05 PM5/21/15
to dc.js user group

Edwin Shin

unread,
May 22, 2015, 3:58:31 AM5/22/15
to dc-js-us...@googlegroups.com
Gordon,

Thanks for taking a stab at this and the pointer to dc.override--I wasn't aware of that.

I'm still stumped as to how to get this to work without hardcoding the div id as you did in your jsfiddle. I've read a couple of Mike Bostock's posts about d3's selections (https://github.com/mbostock/d3/wiki/Selections, http://bost.ocks.org/mike/selection/), but from the context of my original fiddle (https://jsfiddle.net/9fhywdu8/1/), I can't figure out how to select the box-header div.

As I mentioned in my original post, I'm using dc.js to render a bunch of charts (i.e. there will be many .box-header divs). Seems like I'd need some d3 selection logic that lets me navigate from _root to parent (.box-body), to parent again (.box), and then down to child (.box-header).

Am I even on the right track?

Thanks,
Eddie

Edwin Shin

unread,
May 22, 2015, 6:54:38 AM5/22/15
to dc-js-us...@googlegroups.com
Well, with a bit more hair-pulling I managed to derive the right d3 selection incantation:

d3.select(chart.root()[0][0].parentNode.parentNode).selectAll('.reset').style('display', null);


Gordon: I realize I didn’t actually understand your earlier attempt to provide guidance on how to override this behavior for the general case. If I want to override turnOnControls and turnOffControls for my entire app (i.e. for all charts, not just specific ones) how exactly ought I be using dc.override?

Thanks!

Gordon Woodhull

unread,
May 22, 2015, 8:26:13 AM5/22/15
to dc-js-us...@googlegroups.com
It's true, I didn't really address the general case. I think you are best off just defining a function which applies these overrides, and then calling the function for each chart. Since dc.js does not use prototypes, there is no way that I can think of to change the base behavior for all charts without changing the dc.js code. dc.override really only helps change a method in a way that the original is still accessible, so it probably isn't relevant here after all.

Your solution looks about as good as I would expect. You can probably replace [0][0] with .node() though.

Another possible approach so you don't need to do all this, is to add your header to the same div you pass to dc, since dc just appends an svg to the div. (Thinking inside the box, heh.)
--
You received this message because you are subscribed to the Google Groups "dc-js user group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-gro...@googlegroups.com.

Edwin Shin

unread,
May 22, 2015, 8:58:19 PM5/22/15
to dc-js-us...@googlegroups.com
On 22May, 2015, at 8:26 PM, Gordon Woodhull <gor...@woodhull.com> wrote:

It's true, I didn't really address the general case. I think you are best off just defining a function which applies these overrides, and then calling the function for each chart. Since dc.js does not use prototypes, there is no way that I can think of to change the base behavior for all charts without changing the dc.js code. dc.override really only helps change a method in a way that the original is still accessible, so it probably isn't relevant here after all.

I ended up with the following:

// Monkey patch to override behavior of turn(On|Off)Controls
dc.baseMixin = (function(original) {
  return function (_chart) {
    var result = original.apply(this, arguments);
    
    _chart.turnOnControls = function () {
      var grandparent = _chart.root().node().parentNode.parentNode;
      var _filterPrinter = _chart.filterPrinter();
      d3.select(grandparent).selectAll('.reset').style('display', null);
      d3.select(grandparent).selectAll('.filter')
        .text(_filterPrinter(_chart.filters())).style('display', null);
      return _chart;
    };
    
    _chart.turnOffControls = function () {
      var grandparent = _chart.root().node().parentNode.parentNode;
      d3.select(grandparent).selectAll('.reset').style('display', 'none');
      d3.select(grandparent).selectAll('.filter')
        .style('display', 'none').text(_chart.filter());
      return _chart;
    };

    return result;
  };

})(dc.baseMixin);


This seems to do the trick (see updated fiddle: https://jsfiddle.net/9fhywdu8/7/). Any suggestions/feedback on this strategy would be appreciated.


Your solution looks about as good as I would expect. You can probably replace [0][0] with .node() though.

Thanks for the tip, .node() worked great.

Another possible approach so you don't need to do all this, is to add your header to the same div you pass to dc, since dc just appends an svg to the div. (Thinking inside the box, heh.)

Assuming I understood your suggestion correctly, I gave it a shot: https://jsfiddle.net/9fhywdu8/5/. Seems to work! For consistency with the dashboard layout I’m using (as well as some weird rendering issues with Leaflet + dc.js when cramming text, reset links and maps all together) I wanted to keep the charts and/or maps in a separate div from the header.

After all of this, I’ve come round to believing it would be desirable to have turn(On|Off)Controls take an optional selection or node argument, to support the use case of folks who need the reset/filter controls to be outside the chart or child of the chart div. But I don’t know if I’m a minority of one in this case.

Thanks again for all your help. I was getting a bit lost there for awhile. Now I can go bang my head on other issues ;-)




Gordon Woodhull

unread,
May 22, 2015, 9:26:36 PM5/22/15
to dc-js-us...@googlegroups.com
Hahaha. That's awesome. JavaScript is endlessly mutable like that!

I guess the real question is, what is the feature request here? I think if you were able to specify a modifier function to be applied to root before these elements are selected, that would do it. Or even a controlRoot parameter that just uses chart.root() by default.

I'm not sure if it's worth it though. Is this something others have run against?

Thanks for sharing your solution, Edwin!

Cheers
Gordon
--
You received this message because you are subscribed to the Google Groups "dc-js user group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-gro...@googlegroups.com.

Chris Wolcott

unread,
May 5, 2020, 11:45:29 PM5/5/20
to dc-js user group
I have upgraded to dc.js 4.0.0 can anyone show me how to provide an override for dc.js 4.0.0
To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-us...@googlegroups.com.

Gordon Woodhull

unread,
May 5, 2020, 11:50:20 PM5/5/20
to dc-js-us...@googlegroups.com
dc@4 uses ES6 classes. Anything wrong with creating a derived class from whatever chart you need, and overriding the methods? Or am I missing something?

On May 5, 2020, at 11:46 PM, Chris Wolcott <wooly...@gmail.com> wrote:


To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-gro...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dc-js-user-group/841595bb-691d-40bf-a379-78d736d2faba%40googlegroups.com.

Chris Wolcott

unread,
May 6, 2020, 10:27:33 AM5/6/20
to dc-js user group

Thanks for the hint.  I was still looking at overriding functions rather than extending classes.  It was nice before because I could just override the functions in baseMixin and that would work for all the charts.  But now I need to extend each chart that I use and define the new methods.  This is what I did for PieCharts since this is the first chart that I am working on,

class PieChartOV extends dc.PieChart {

   
// Override behavior of turn(On|Off)Controls
    // By default the RESET and FILTER controls have to be placed in the CHART DIV.
    // Because we are using KEEN Dashboards and BOOTSTRAP they look better in the
    // TITLE DIV and thus we had to change the logic of where to find the controls.
    // See https://groups.google.com/forum/#!topic/dc-js-user-group/h7cRqozt_Mc
    // The original overrides were against dc.js v2 and also worked for v3
    // dc.js v4 changed the architecture.

    turnOnControls () {
       
if (this._root) {
           
const grandparent = this._root.node().parentNode.parentNode;

           
const attribute = this.controlsUseVisibility() ? 'visibility' : 'display';
           
d3.select(grandparent).selectAll('.reset').style(attribute, null);
           
d3.select(grandparent).selectAll('.filter').text(this._filterPrinter(this.filters())).style(attribute, null);
       
}
       
return this;
   
}

   
turnOffControls () {
       
if (this._root) {
           
const grandparent = this._root.node().parentNode.parentNode;
           
const attribute = this.controlsUseVisibility() ? 'visibility' : 'display';
           
const value = this.controlsUseVisibility() ? 'hidden' : 'none';

           
d3.select(grandparent).selectAll('.reset').style(attribute, value);
           
d3.select(grandparent).selectAll('.filter').style(attribute, value).text(this.filter());
       
}
       
return this;
   
}
}

Then to create the chart I call


var categoryPieChart = new PieChartOV('#chart_category');

Screen Shot 2020-05-06 at 10.25.11 AM.png

Gordon Woodhull

unread,
May 6, 2020, 12:31:53 PM5/6/20
to dc.js user group
Hi Chris,

That's a really good point. 

TIL it is possible to change the BaseMixin prototype, which will affect all the charts:

dc.BaseMixin.prototype.turnOnControls = function() {
      // ...
};

It's not necessary here, but for completeness, if you need to call the original function you can store it and call it like so:

var origTurnOnControls = dc.BaseMixin.prototype.turnOnControls;
dc.BaseMixin.prototype.turnOnControls = function() {
    // ...
    return origTurnOnControls.call(this);
};


Cheers,
Gordon

To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-gro...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dc-js-user-group/4b2aa001-490e-464e-8248-b9f18901681d%40googlegroups.com.

Chris Wolcott

unread,
May 6, 2020, 10:21:18 PM5/6/20
to dc-js user group
Thanks Gordon.  I changed my override based you second suggestion.  It works perfectly.  Sorry for the question that I should of figured out on my own.

Gordon Woodhull

unread,
May 6, 2020, 10:29:03 PM5/6/20
to dc-js-us...@googlegroups.com
No problem. 

It’s a good thing to know, and might be worth documenting in the upgrade guide or somewhere else. 

I’m happy to see that it’s so much easier than before. I wasn’t totally sold on prototype inheritance but it has turned out pretty good.


On May 6, 2020, at 10:22 PM, Chris Wolcott <wooly...@gmail.com> wrote:


To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-gro...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dc-js-user-group/2d58ec00-0156-4787-8b3b-fc3ce686bd80%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages