artem

artem

Hi All

Context
I am creating a small DB analysis tool in LiveBook to explore what we have in our job matching service. Conceptually this is just several relatively simple cells:

  1. Select a job opening
  2. Select an applicant
  3. Display something about applicant

Choose in the previous cell, update in the next cell helps
People at Select something in one LiveBook cell, update list in another cell - #3 by hugobarauna helped me to figure how to use Kino.listen so that “Select an applicant” set was able to react to choices done in the “Select a job opening” step and show the applicant list only for the selected opening.

It works very well except for the first render of the “Select an applicant step”

The problem
By the moment of the very first render of “Select an applicant step”, job opening selector could be clicked multiple times already and I wail to figure how to pass the current value further (except via ssome external storage such :ets , but that sounds like an ugly overkill).

If that was a LiveView, I would just update process assigns from async callback.
What would be the way to make it work in LiveBook? Is there some sort of “current book context” that could be accessed and updated somehow?

Here’s code of what I want to achieve in a very simplified way:

First cell:

# First cell

button = Kino.Control.button("Click")
counter = 0

Kino.listen(button, fn _event ->
  IO.puts("button clicked, counter is #{counter}")

  # Obviously it won't work as it just createa a local variable,
  # it can't update the earlier defined one
  counter = counter + 1
end)

button

Second cell:

# Second cell should be able to set current counter value somehow
IO.puts("Counter as set by the moment of this cell evaluation:#{counter}")

Kino.listen(button, fn _event ->
  IO.puts("Do something with the current counter of #{counter}, maybe increase it as well")
end)

Showing Posts 3 to 1

jonatanklosko

jonatanklosko

Creator of Livebook

There are two paths you can take to write an app:

  1. Separate cells, relying on Kino.Input.read and automatic cell reevaluation. Whenever the input value changes, the dependent cell reevaluates. The benefit in your case is that while a cell is evaluating, it doesn’t matter how many times you change the select value, the next time the cell reevaluates it will just get the latest one.

  2. Using forms and events. When the form is dynamic, you basically want a GenServer with state that manages form events and renders UI into the frame. The idea with the new Kino.Screen abstraction is to make this workflow easier. Since mentioned Agent and shared state, using a single process is a more natural abstraction in this case. This also implies writing pretty much everything in a single cell, which I agree diverges from how you would write a typical notebook, but it is somewhat inherent for a stateful, event-driven app.

From a more technical perspective, the core principle in Livebook notebook evaluation is reproducability. Reading input values and reevaluating cells fits into this model (where inputs are conceptually similar to variables assigned via UI). On the other hand, when we think about events such as button clicks and form submissions, we shift to a different paradigm.

Both 1. and 2. have their use cases. One of the key differences is that with 1. the inputs are shared across everybody entering the page, while with 2. we can build a single app that handles multiple users, and provides each of them with separate interactions, but can also make the experience shared (e.g. a chat).

Ideally we would he a single way of doing things, but so far the consensus has been that each of them has a place.

artem

artem OP

Querying is fast (well, fast enough for my research trials), I was just annoyed that I cannot pass value set on button click from cell A, to cell B except if I just command from cell A’s Kino.listen to re-render cell B’s frame. And for cell B frame to be available from cell A I’ll need to create modules, reorder stuff carefully.. lots of action for something that IMHO should be simple exactly in notebooks created for analyzing data.

By now I have half-solved it by using Agent as a centralized data storage from notebook.

Now cell B can also subscribe to cell A’s clicks, but actually get it’s data from the Aagent.

It still causes a bit of mess, because cell B code needs to be somehow activated when it needs to refetch Agent, but somewhat works.

Or is there possibly a way to somehow subscribe (or listen) in Livebook for the Agent state changes? Such reactivity would make my code way simpler.

jonatanklosko

jonatanklosko

Creator of Livebook

Hey @artem!

Is the issue that reacting to “select a job opening” takes time (querying data), so the user can change the value multiple times, while the data is still loading?

One way to solve this directly would be to use Kino.listen/3 with state. You would do the query in a Task.async, store the task in the state, so if another event arrives, you can kill the existing task (if applicable) and start a new one.

Another way would be to manage the whole form as a single frame and have a GenServer rendering into it. With that you could render “Loading…” or similar after changing the value.

There is a PR in Kino that you can give a try: https://github.com/livebook-dev/kino/pull/489 ({:kino, github: "livebook-dev/kino", branch: "jv-kino-screen"}). It adds an abstraction for building more complex event-driven forms/wizards.

— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
ChrisAmelia
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication): toke...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews