A viewof pedagogical notebook

Hi everyone! In my continuing saga of translating index.html's into observable notebooks I want to create buttons that impact the plot - and this finally forced me to actually try to grasp what viewof actually does

In the little notebook here I leveraged viewof to get my little circles to change color on click. It seems like viewof is used to enable what I first tried to do - create a global function to be used with an event handler.

I guess I was wondering if there are ways I can improve on this to make why or when you’d use viewof more obvious? Or maybe I’ve missed the mark on what viewof does entirely?

I think a little notebook like this will be helpful in the “translation” of observable-ifying d3 (at least for me :blush: ) so any feedback is appreciated! Thank you !!

1 Like

My attempt at a “light-weight” explanation:

4 Likes

Hi @Maya !

I took a look at your notebook and it seems that viewof doesn’t actually play a role in the functionality there. Try commenting out your viewof cell and note that the circles still change colors when you click the buttons.

Here’s a fork of your notebook which does use viewof to achieve the color-changing functionality, though the pattern of exporting functions might seem a little abstract if you haven’t seen it before:

1 Like

OMG LOVEEE THIS - incredibly well written too wowowow not only did this help to have viewof click but also totally sheds light on how to convert the use of viewof to non-observable context (or vice versa)

But there is a problem: If we enter a different value, it doesn’t update the cell. Observable only tracks changes to the top-level object or value. It can’t detect changes on individual properties.

I think not understanding this was the crux of my issue and how/why my little changeColor() function was not working as expected?

Anyways thank you so much this article is gold!!

1 Like

OMG :sob: this is PERFECT there is so much to unpack in here! I love that the functions scope is within the single plot and that you’re actually using viewof here!

So just to confirm me mental model, viewof is not confined to buttons/select dropdowns/forms but you can make a viewof out of any object as you did here – where the viewof object is our d3 plot which has a changeColor function that we export.

I hope this isn’t too dumb a question but I am curious here how changeColor becomes a method of color_chart (I mean, how is it that we can call color_chart.changeColor how is it not color_chart.value?

Anyways thank you so much for this its incredibly helpful!

1 Like

This is part of the magic of the viewof keyword. Let me quote from the notebook that @mootari linked above:

By prepending viewof to the cell name, we tell Observable

  1. that our cell contains a DOM element (the text input),
  2. and that Observable should check the .value property to find the actual cell value.

Let me spell these points out a bit more explicitly. Defining a notebook cell using e.g. viewof cellName creates two variables that you can refer to elsewhere in the notebook:

  1. The first is named “viewof cellName” and consists of the return value of the cell, (usually a DOM element). This is what you might expect for a normal named cell in a notebook, except for the unusual fact that the name consists of two words.

  2. The second variable is one named cellName. This variable is equal to (viewof cellName).value (which we can equivalently write as viewof cellName.value, since the parser is smart enough not to interpret that as viewof (cellName.value)), so you can kind of think of cellName as shorthand for that longer expression (though there are some more details about how cell dependency updates get triggered that I won’t get into here.)

So, returning to your question, we have that color_chart == viewof color_chart.value and color_chart.changeColor == viewof color_chart.value.changeColor.

Does that help?

The examples in the first few notebooks of the official documentation collection might also be useful:

https://observablehq.com/collection/@observablehq/views-interactivity

1 Like

* Any object that “implements the EventTarget interface”. Which means it offers all the methods which the interface describes: .addEventListener(), .removeEventListener() and dispatchEvent().

All DOM elements implement this interface, and you will probably never use any other type of object for a view. :slight_smile:

3 Likes

Thank you all for this thread. What I enjoy the most (other than learning more about `viewof` specifically, which I very much enjoyed) is the different terminology used both to frame and answer questions. I lack this technical vocabulary, which (for me) results in confusion while learning: the inability to ask a question ‘correctly’ or to find a result that I am after. Thank you @Maya for this question and @bgchen and @mootari for these responses!

P.S. - I also appreciated use of ‘Show and Tell’.

1 Like

I’ve remember learning about this (also about viewofs) on this “hidden” notebook from @tom

In his example he adds the EventTarget properties to a Date object, so a viewof would present the date on the inspector but a different viewof value

//from https://observablehq.com/@tmcw/exploring-viewof
viewof number = {
  let output = new Date();
  let listeners = [];
  output.value = output.getSeconds();
  output.addEventListener = (listener) => listeners.push(listener);;
  output.removeEventListener = (listener) => {
    listeners = listeners.filter(l => l !== listener);
  };
  return output;
}

I’d be curious to learn other examples this would be useful

3 Likes

Ahhhh thank you @bgchen! Spelling it out like that in conjunction with @mootari’s explanation finally made this click! The little peek under the hood of what the “magic” is was super helpful :slight_smile:

2 Likes

Here’s an example of a custom object for a view: @mbostock/synchronized-views#View. In use: @jrus/bezier-segment

3 Likes

I knew I should have clarified: “Unless you use a view in a pub/sub fashion”. :slight_smile:

It should also be noted that you can extend EventTarget instead of reimplementing the whole interface (unless you want to target Safari 13 or below, which doesn’t support the EventTarget constructor).

2 Likes

Whoa whoa whoa what is this wizardryyyyy this is wild amazing thanks for sharing!

1 Like