dc-js / dc.js

Multi-Dimensional charting built to work natively with crossfilter rendered with d3.js
Apache License 2.0
7.43k stars 1.8k forks source link

LineChart's lines disappear after a redraw after adding to crossfilter #1672

Closed kunxin-chor closed 3 years ago

kunxin-chor commented 4 years ago

The source file can be found here: https://dc-linechart-redraw.kun-xinxin.repl.co/

Use Case: I am trying to add to a crossfilter one more data point. After which, I redraw both the line and pie chart.

Expected: The line chart have an additional data point.

Output: The pie chart updates correctly; the line chart doesn't show its line anymore.

Here's how the chart is defined (it is drawn successful before adding to the crossfilter)

 chart = new dc.LineChart('#line-chart')
        .width(500)
        .height(300)
        .dimension(dim)
        .group(group)
        .x(d3.scaleBand())
        .xUnits(dc.units.ordinal)
        .brushOn(true)
        .xAxisLabel('Fruit')
        .yAxisLabel('Price')
        .ordinalColors(d3.schemeSet1);

        chart.render();

The event handler for redrawing:

$("#load-more-btn").click(function(){

    // cf.remove(()=>true);
    cf.add( [
    {
       "name" : "Durian",
      "image" : "https://upload.wikimedia.org/wikipedia/commons/thumb/c/cb/Pineapple_and_cross_section.jpg/286px-Pineapple_and_cross_section.jpg",
      "price" : 300
    }
  ])
    chart.render();
    pie.render();
})
gordonwoodhull commented 4 years ago

I think this may be related to #598.

It is easy for dc.js to get confused by ordinal line charts. (Ordinal line charts are somewhat confusing in general, since "ordinal" usually means "categorical" and there isn't any particular ordering to categories, whereas a line has a particular ordering.)

You can fix your example with any of the following changes:

On initialization:

chart.elasticX(true)

On adding data:

chart.x().domain([])

or

chart.x(d3.scaleBand())

You may also want .elasticY(true) because the new point is outside the original Y domain.

Should this happen automatically? Yes, but it's tricky because people do set the domain manually, so we can't just reset it every time the chart is rendered.

gordonwoodhull commented 4 years ago

Digging into it a little deeper, when an ordinal coordinate grid chart is rendered or redrawn, it will check if elasticX is true or the X domain is empty. If so it will calculate it:

https://github.com/dc-js/dc.js/blob/e391b99281b6c48733395c70a05c4a5489b3e4d3/src/base/coordinate-grid-mixin.js#L468-L470

In this case, since the chart has already been rendered, its X domain has already been calculated here, so the chart trusts that the X scale is all set up and skips this step. Later, when it tries to map the new value to an X coordinate, it gets NaN, which causes it to sanitize the data to empty.

IIRC this was slightly more graceful in DCv2 / D3v3, because d3.scale.ordinal() used to automatically add points if they didn't exist. (However, I think it added them at the right side of the chart, causing a very ugly line chart in cases like this.)