assign global variable from within a chart

Hi! I absolutely LOVE observable, especially connecting it with R. In my simple notebook I’d like to convert the two console.log statements to the global assignment to a as this would allow me to use a inside R via robservable https://observablehq.com/@mayagans/histogram

Anyone have any tips/suggestions?

Here’s a suggestion using mutable:

Slightly more preferred would be to define a view, which you can read about here:

omg @mbostock thank you :sob: curious though, does assigning to a mutable also return the function because in the code above the bars no longer turn blue on click Im wondering if thats because mutable exits the function execution?

No, no magic there. I think that’s a separate bug in the notebook. :slightly_smiling_face:

1 Like

Sorry to bother you with something so trivial - I’ve been poking at this for an hour and the ifelse logic DOES work when I comment out the mutables so I’m not sure how to address the bug separately… I tried making the mutable part and the color logic separate but honestly not sure that changes the code at all…

      .on("click", function(d, i) {
      let brr = Array.from(i)
      console.log(d3.select(this).style("fill"))
      d3.select(this).style("fill", "red")
      // whoa this says the fill is now red but it's not?!?!
      console.log(d3.select(this).style("fill"))
      changeA(brr)
  })

The problem is that changeA references a, which means that changeA is re-evaluated whenever you assign a new value to mutable a = …. And because chart references changeA, the whole chart gets redrawn, too.

In general, any cell that assigns to a mutable should never read that value reactively; it should reference mutable a instead of a. That’s true for anything upstream of a cell that assigns to a mutable, too.

Anyway, this is what I was hinting at in saying that views are preferred to mutables, because it’s really easy with mutables to create a circular dependency or for unexpected cells to run when you assign to a mutable. Take a look at this example for a more idiomatic way for a chart to expose a value:

1 Like

Here’s what I recommend using viewof:

The key part is here in the click listener. Here’s a commented version:

.on("click", (event, d) => {

  // If the selection is already the current bin (d), then
  // revert the selection back to the entire dataset. If
  // preferred, you could change this logic to allow the
  // user to toggle individual bins on and off.
  selection = selection === d ? data : d;

  // Now that the selection is set, recompute the fill color
  // of the rect elements. By removing the fill attribute for
  // unselected bins (i.e., by returning null), these bins will
  // inherit the fill attribute of the parent G element, so we
  // don’t have to repeat the gray color. Also, note that we
  // reference the rect selection explicitly, rather than re-
  // selecting elements through the DOM (which is an anti-
  // pattern in Observable because it can select DOM elements
  // created by other cells, and because the DOM created by
  // other cells can be dynamic!).
  rect.attr("fill", d => selection === d ? "blue" : null);

  // Lastly, update the SVG element’s value property, which is
  // how you define the value of a view, and then dispatch an
  // input event to tell Observable to re-run any cells that
  // reference the view’s value (i.e., selection).
  svg.property("value", selection).dispatch("input");
});

Oh, also I changed the d3.bin definition so that the contents of the bins are the elements in your data, rather than the values (counts).

bins = d3.bin().thresholds(th).value(x => x.count)(data)

For more, see: