how to control cell updating in response to value changes in dependency cells?

I am a little stuck on how to do this and am hoping that someone who is familiar with the Observable update cycle may be able to suggest a solution.

This is the html output of a cell:

As you can see this cell uses viewof, and its value is set by the custom double handled range slider on the right.

The ‘Time Period 2’ readout is set by that UI element (timeRangeSlider2) and the ‘Time Period 1’ is set by the values of another cell in the notebook that uses another instance of that same double handled slider. (timeRangeSlider1)

My code for this cell is schematically like this:

viewof multiStateBarSelections = {
  const div = makeDiv();
  makeSlider(div); //makes timeRangeSlider2
  // in makeSlider, an event listener is added and the slider element is appended to div like this:
  // const slider = getSliderElement();
  // slider.addEventListener('change', updateValueOfDiv(); div.dispatchEvent(new CustomEvent('input')));
  // updateValueOfDiv();
  // div.appendChild(slider);

  const timeReadouts = html`<p>otherCell.startDate otherCell.endDate</p><p id='second-time-readout'>div.value.startDate div.value.endDate</p>`;
  div.prepend(timeReadouts);
  
  div.addEventListener('input', () => timeReadouts.select('#second-time-readout').text(div.value.startDate div.value.endDate) //updates Time Range 2 readout

  return div;
}

The issue I’m having is that while I can get timeRangeSlider2 to update the Time Range 2 readout, if I change the timeRangeSlider1 in its cell (elsewhere), then, because this cell is dependent on the value of that cell, the entire cell reruns, which remakes the timeRangeSlider2 because it calls makeSlider again, and the selection that was previously made with timeRangeSlider2 is lost.

I am wondering if anyone has any thoughts on how to handle this?

I can share the notebook if that is helpful but you won’t be able to see it populated with data.

Thanks for any thoughts!

1 Like

As convenient as I find the viewof operator mixed with handy inputs, I very often find that I need to write code that manipulates things more fundamentally than the Observable reactive paradigm allows.

For example, I might want to modify some element of an SVG according to the change in some parameter controlled by a slider. Rather than using reactive programming, I might assign a function to the oninput event of the slider. This function can then select the SVG and modify any object by class or id within that SVG that we want.

There are a variety of reasons this might be necessary - one of which is to deal with interrelated dependencies like you describe.

I have actually already done this for elements within a cell, but can I add an event listener to another cell (cell B) and then also stop the propagation of cell B’s custom “Input” event that occurs when cell B changes and triggers a rerun of cell A?

screenshot of code in case it’s helpful…(cell B = choroplethSelections)

A link to a shared notebook (not necessarily “published”) would be very helpful for folks interested in diagnosing this. :slight_smile:

Sure thing! As I mentioned, you won’t be able to load data (which means nothing will populate) and this thing is kind of the size of a small application, but, here it is. https://observablehq.com/d/9f6ac5a2f9c7806e

yo!

Synchronized Views may be a great option here. They’re great when you have some viewof cell that can be updated from various other cells. But, one thing that you’ll want to avoid (that you mentioned) is directly referencing the value cell in any synced “viewof” cell, because any input will trigger a re-execution that makes interaction weird. This is an example of what that looks like:

link: https://observablehq.com/d/a60a5a2442c039ff

Notice how when you change the slider value, the slider cell re-executes, bluring the input, making it impossible to cleanly “slide” through values.

What you can instead do, however, is expose an “update” function on any viewof cells that will surgically update the cell’s DOM to insert the value of the value cell back where you want, avoiding an unnecessary re-execution. This is what that looks like:

link: https://observablehq.com/d/a60a5a2442c039ff

Notice how changing the slider here is more fluid, it doesn’t blur on change, and the value to the left of the slider updates normally.

Also, the synchronized views notebook is written with raw <input> elements in mind, so if you are using a very customized viewof cell (which that very cool looking time period selector looks like), you might need to update that cell’s API to match the set() logic in @mbostock/synchronized-views

2 Likes

@asg017 Isn’t there an easy way to just make a chart?!

I’m kidding! The Synchronized Views notebook, which I remember looking at a while ago and stopping once I got to the part about mutables, looks like it will indeed be super helpful. Thank you for realizing that it would help and pointing it out and making such a nice example!

1 Like

Another option would be to read either this.value or this in the cell that displays both sliders. If that cell is being re-run, this contains the previous value of the cell.

1 Like