Noob question - buttons to increment / decrement a "global variable"

I appreciate all of the thorough documentation and discussions on viewOf and mutables, but I’m still confused, so I’m definitely missing some basic principle.

I have a data object: a story with multiple pages. When I load the notebook I want to set the current page to 1, and allow the user to clic “next” and “previous” buttons to change the currentpage.

my buttons using the “viewOf” do appear to trigger the functions, but I can’t seem to change the value of currentPage. Any clues?

https://observablehq.com/d/b5644d2750d76de4

Thank you!
Bruce

Hi Bruce,
Could you please make the notebook visible to the public?
Sharing button → set to Public - unlisted
Thanks

As pointed out by Cobus your notebook is currently not accessible, so I’ll just share a possible solution here:

function createPager([min, max], {value = min} = {}) {
  const prev = htl.html`<button onclick=${() => update(value - 1)}>Previous page`;
  const next = htl.html`<button onclick=${() => update(value + 1)}>Next page`;
  const current = htl.html`<output>`;
  const view = htl.html`<div>${prev} ${current} ${next}`;
  update(value);
  return Object.defineProperty(view, "value", {
    get: () => value,
    set: update
  });
  
  function update(newPage) {
    current.value = value = newPage < min ? min : newPage > max ? max : newPage;
    prev.disabled = value <= min;
    next.disabled = value >= max;
    view.dispatchEvent(new Event("input", {bubbles: true}));
  }  
}
viewof pageNum = createPager([1, 5])
1 Like

Ack! Just made public.

Thank you, I will play around with that and see if I can figure out how it’s working, it’s a very different pattern than I was following. Meanwhile I made my notebook public in case you can take a look.

That works, thank you Mootari. FWIW, here are some of the parts of your example which are new to me, and are going to force me get over the hump and learn / read up on: “htl.html”, “() => value”, “dispatchEvent”, {value=min} in the list of inputs, {bubbles: true}, “newPage < min ? min” So, I’ve got a way to go! :slight_smile:

1 Like

Thanks for the list! Let me walk you through those one by one:

  • htl.html is the successor to the html template tag. Whenever you add an HTML cell, it uses htl.html under the hood. You can read more about it here: Hypertext Literal / Observable | Observable
  • ´() => value´ is an arrow function and equivalent to function() { return value }.
  • If you’ve used d3 or jQuery before, you may already know selection.on(“eventName”, handler). That’s “syntactic sugar” on top of element.dispatchEvent. We call it here to let Observable’s Runtime know that the view’s value has changed.
  • {value = min} destructures the key “value” from the passed in object and saves it to a variable “value”. If the key does not exist, it instead receives “min” as its default value. Read more about destructuring assignments here: Destructuring assignment - JavaScript | MDN
  • {bubbles: true} isn’t strictly necessary here, but good practice. It ensures that the “input” event can be listened to even if the pager element is nested inside another element. Read more about it here: Introduction to events - Learn web development | MDN
  • newPage < min ? min is called the “ternary operator” and can be read as if/else. In this case we clamp “value” to not exceed either “min” or “max” by chaining multiple if/else conditions. This can also be written (imo less intuitively) as Math.max(min, Math.min(value, max)). Read more about it here: Conditional (ternary) operator - JavaScript | MDN

A while back I also wrote a short introduction to viewof. Perhaps you find it helpful as well:

Oh, and if you have more questions, please don’t hold back! :slight_smile:

3 Likes

That was really helpful, Thank you so much!

1 Like