Reactively update content within the same cell?

I’m trying to make a quiz like Quiz / Nikola Toshev / Observable

I have a bunch question and the answer key for each in an array. After clicking a button, I want to indicate for each answer if it was correct or not. I understand how to do this if each question was in a separate cell, but not if they are all in one cell like in the notebook. Tried Inputs.input() depending on the Input.checkbox() that has the answer and similar things, but can’t seem to reach a working solution. Any help would be appreciated!

See how @patrickmineault uses the ‘Reset’ button in this notebook with the ‘set’ function: Leaky integrate-and-fire (LIF) neuron with balanced excitation and inhibition / Patrick Mineault / Observable

This seems similar to Synchronized Inputs / Observable / Observable

I’ve tried this, but it seems to require each input element to be in its own cell - you cannot declare viewof xyz within a function. The issue is not the button triggering the action, it is synchronizing a checkbox with an html text in the same field.

It looks like some additional methods/properties have to be added to the API to allow for run-time update of multiple inputs hosted in a form.

you can achieve what you want with Composing viewofs with the view literal / Tom Larkworthy / Observable

I am quite good at composing these things, my current state of the art is the UI for Simple notebook storage: cellstore / Endpoint Services / Observable which is done with a single call to Squeezing more Juice out of UI libraries / Tom Larkworthy / Observable. cellstore has a UI playground I use to skaffold to UX Simple notebook storage: cellstore / Endpoint Services / Observable

with juice you can take “a function of parameters to a static render of HTML e.g. (name, age) => htmlYour name is ${name} your age is ${age}” to a reactive version composite version where the function parameters are (bindable) subviews of a larger view

juice((name, age) => html`Your name is ${name} your age is ${age}`, {
    name: '[0]',
    age: '[1]',
})

view has the most flexibility. Juice is a wrapper I originally made to change how existing UI components work but I have since realised its very fast for making reactive UI’s from scratch.

2 Likes

Thank you for responding @tomlarkworthy . Mere composition of Inputs seems to be resolved with Inputs.form() (that apparently was implemented after your view library). But, neither Inputs.form nor the view literal seems to be able to have one Input control updating another in the same composition hierarchy. Even trying to refer to another control gets a “circular reference” error. Do you have a different idea? (I looked at juice but didn’t find this implemented either.)

It seems that there should be a way to have views and register events properly, but I can’t get it right.

OK, I managed to update peer controls with Inputs.bind like this:

function question(txt, answerKey) {
  const chk = Inputs.checkbox([txt], {unique:true, label: 'Check if true', valueof: x=>!!x});
  return Inputs.form([
    chk,
    Inputs.bind(Inputs.text({label: "Result", placeholder: "placeholder"}), chk),
    htl.html`<br>`
  ]);
}

(updated the notebook Quiz / Nikola Toshev / Observable)

Now, I need something slightly different because:

  1. I want the “binding” to depend on other factors, so make it an arbitrary function, and
  2. I want binding to update raw html instead of an input

I think I can fork the Inputs.bind() implementation to do these.

Hi I have two versions linked here Community Help / Tom Larkworthy / Observable

Thank you @tomlarkworthy ! This seems to be what I want with 1). I still have difficulties when trying to add a submit button that enables evaluation of responses - it clears the user selection for some reason!

See the forked version Community Help / Nikola Toshev / Observable

I want the answer to be evaluated as correct/incorrect after the user presses the “Submit” button, but for some reason this clears the answer that the user has selected…

You are very close. The UI ends up with a dataflow dependency on submitted, which means when the submitted value changes the UI containing cell reruns from scratch

Try “viewof submitted.value” instead, which put the dependency on the DOM node not the inner value, and reads the inner value through the DOM node instead.

views are really two variables

Actually I wrote that on the bus without thinking properly. For submitting it I would let the value pass through the submit button via the reduce function, so the submit is a gate.

You could roll that approach up into a single cell too

Hmmm…

When I do what you suggested initially, the composite control doesn’t update when I click submit. With your subsequent suggestion, the composite control shows the correct/incorrect feedback even before the Submit button is clicked, and I can’t add the filtering condition because it creates a circular definition…

It seems unavoidable for the control to lose user input state when a separate dependency has changed…

OK, so if you want to be able to read and write to a composite function without invalidating the whole thing, I think you pretty much hit the limit of Inputs.form and you need view, because with view each subfield becomes its own subview so it supports fine grained binding and accessing individual components after the composite is constructed.

With view you can more easily listed to just the submit, and update just the response by reading just the answer and without going through dataflow by using the viewof ui.<subcomponent> method of access. See

Oh funnily enough I developed view for a survey application too…