Why will a graph render after running function, but not on load?

Hi Community.

I encountered a weird behavior and I was hoping someone might help by explaining why (and how to avoid):

I created a dual-access bar chart using C3.js:

When I load the notebook, I get a TypeError message:

However, when I run the function, the chart appears:

Any insights into what’s going on?

With thanks,

Aaron

There are a few things wrong here.

The first is that your budget_FY18 cell has an implicit dependency on the budget cell where you declare the DIV. It’s implicit because you reference the DIV through the DOM by its identifier (#budget), but Observable only understands explicit references. So, if you replace

bindto: "#budget"

with

bindto: budget

Then Observable will understand that budget_FY18 depends on budget when constructing the computation graph, and run them in the correct order.

Similarly, C3 doesn’t properly declare its dependency on D3. Instead, it assumes that a d3 global is available before C3 is loaded (even in an AMD environment). That’s why you’re seeing “Cannot read property ‘scale’ of undefined”: C3 is trying to accessing d3.scale before d3 has been defined. This would best be fixed by a pull request to C3, but you can workaround it by changing how C3 is loaded:

c3 = {
  window.d3 = await require("d3@3");
  return require("c3@0.4");
}

I also would suggest that you use the latest version of C3 (0.6) which is compatible with the latest major version of D3 (5).

Another thing I would fix is to avoid mutation across cells. Instead of declaring a DIV in one cell, and then populating the contents of that DIV in another cell, you should create and populate the DIV in the same cell. There’s no reason to use the old jQuery-style of DOM mutation in Observable; it’s cleaner to instead create the DOM you want to see, and then return it.

I’ve packaged all of these suggestions up into a reusable notebook which you can import:

2 Likes

Thank you, Mike! As always, you’re frighteningly quick to respond and very clear and articulate about underlying issues. I really appreciate your care and attention, and am thankful to have this opportunity to learn from you and your work!

3 Likes

I simplified your c3 js notebook for kicks and giggles here if that’s your gist idea of making live charts :slight_smile:

1 Like

Fun! Thank you!

1 Like

Hey @mbostock, I have a similar issue in my notebook, but I couldn’t find what I did wrong. Can you help me to identify what I need to change to make the chart render on load?

Thanks,

Gabe

Welcome @gabovailati! Here’s a fork of your notebook that fixes the issues:

A few notes:

  • The way that your original data and generate_data cells were set up, the notebook would proceed with evaluating the dependencies of data before it was actually processed by the callback in d3.csv in generate_data. This was the root cause of the issue you were seeing. I rewrote data using async so that the cell evaluation order is now more deterministic.
  • I changed the document.querySelector in your picasso.chart cell to a direct reference to a named container cell. See the “Observable anti-patterns and code smells” tutorial notebook.
  • Your notebook wasn’t working in Firefox because the new Date constructor is stricter about what it will accept there than in Chrome. I’ve switched that to d3.timeParse which does the job better.
2 Likes

Thanks, @bgchen!!! Really appreciate your help. I’m still getting used to Observable and your tips helped me to fix my notebook.

3 Likes