how to bundle chart arguments and methods like a "class"?

Hello. I’m trying to re-use the nice histogram here, which has a lot of arguments (e.g. margin, axis defs) in cells external to the chart. I had thought to “bundle” all these arguments into a class, so that those many supporting cells could be reduced to a single cell which I could then re-use across several instances of the histogram chart, by calling a new() class instance within the chart code, and setting the properties within the chart code.

I hope it makes sense what I’m after functionally, which is “bundling” and code management and re-usability – how can I best do that in Observable? I notice that the questions about javascript class in this forum and stackoverflow go unanswered, or are answered with “sorry we don’t do that”, so I think there must be some other way to do it, but I’m not sure what to google/ask.

FYI I have asked this with more explicit code examples at: javascript - In Observablehq, how do I set property value for class in one cell from another cell, in the same notebook? - Stack Overflow .

Here is my toy example…

Here is the test class in cell 1:

class hist_class {
  constructor() {
    this.test_value=null;
  }
  
  set test_value(newvalue) {
    this.test_value=newvalue;
  }

}

Here is my “set” attempt in another cell in the same notebook:

test1 = {

const svg = d3.create("svg")
    .attr("width", 100)
    .attr("height", 50);
  
const hist1 = new hist_class();
hist1.test_value = "test value";

svg.append("text")
    .text(hist1.test_value)
    .attr("x", 10)
    .attr("y", 20);

return svg.node();
  
}

The error I get is “test1 = InternalError: too much recursion.” Because my code is so close to standard examples (e.g. mozilla, w3schools), maybe the error is probably about how observable processes my code, rather than a true .js error.

Thanks for any advice!

As long as your class instance’ state only changes synchronously in your constructor there’s nothing keeping you from using classes.

If however you expect to manage the state asynchronously (e.g. by calling an instance method to update a chart later on), things get more complicated because Observable’s reactivity only works for a cell’s returned value, not individual properties on it. That doesn’t matter though if all you care about is modifying a DOM element.

I think it would help to see a small example notebook which demonstrates the use case that you have in mind.

Thank you! I have updated my question with my toy test that failed in observable before. I appreciate your advice. Btw, the main goal is learning the best way to “bundle” code so that I can re-use it across several notebook cells with minimal visibility and cell management. So if there’s a better way to do it than using classes I’m all ears.

Your problem is that the class definition is calling the setter in the setter.

class hist_class {
  constructor() {
    this.test_value=null;
  }
  
  set test_value(newvalue) {
    this.test_value=newvalue;
  }
}

When you say this.test_value = newvalue, that invokes the set test_value function. This would cause infinite recursion in any JS environment. You need to store your internal state in a separate field. A common way to do that is to prefix the “internal” version with a _.

class hist_class {
  constructor() {
    this._test_value = null;
  }
  
  set test_value(newvalue) {
    this._test_value = newvalue;
  }
}

However, this is a pretty awkward arrangement. I’d advise not using getters and setters for most things unless you are really sure you need them. Simple properties and functions are often simpler.

This class is mostly equivalent to what I think you are getting at:

class History {
  constructor () {
    this.test_value = null;
  }
}

test_value is available on instances of the class for both getting and setting. If you need to run code when test_value changes, then I’d use a function like updateTestValue(newValue) instead, which makes it clear that a side effect is happening.

Thanks this is very helpful. Now I know that it’s a js thing rather than the platform itself. I’ll spend some time with a tutor on js class concepts to figure out the best approach to what I’m after.

Another way is to create a function with optional parameters, and pulling the various helper functions into the single function. See this example (forked from the original histogram notebook).