Skip to content

[Feature Request] Stream of custom events #469

@arathunku

Description

@arathunku

👋 Hi!

I believe this would be a feature request, and I'm unclear whether issue tracker is the correct place to raise this?

Context

Let's say we've 2-3 heavy DB queries and I want to create a report that's available as app. Ideally, automatically refetch the data at some interval or when user clicks a button.

In edit mode, I can select most of the cells as "evaluate automatically" and it's works nicely, but it doesn't work for apps.

Actual solution as workaround

At this moment, I get around this by creating "UI" module that's appending data info in a single frame, but this mode of writing a notebook that also works as an app leaves a lot to be desired. This is also very difficult to explain to people that are just starting with Elixir and want to publish their notebook as an app.

Example:

# one cell, it might also be a more complex form, other type of user input

load_btn = Kino.Control.button("Load data") |> Kino.render()

# another cell
frame = Kino.Frame.new() |> Kino.render()

defmodule UI do
  def show(frame) do
    # very heavy db query 
    results = [1, 2, 3]
    Kino.Frame.render(
      frame, Kino.Markdown.new("#{Enum.join(results, ", ")} #{DateTime.utc_now()}")
    )
  end
end

load_btn
|> Kino.listen(fn _event -> 
  UI.show(frame)
end)
UI.show(frame)
Kino.nothing()

## another cell !

# another cell, wants to access "results" too!
# another cell, it must be reloaded "load_btn" is clicked!

defmodule AnotherUI do
  def show(frame, results) do
    Kino.Frame.render(
      frame, Kino.Markdown.new("Different view #{Enum.join(results, ", ")} #{DateTime.utc_now()}")
    )
  end
end

another_frame = Kino.Frame.new() |> Kino.render()

load_btn
|> Kino.listen(fn _event ->
  # no way to get "results"?
  AnotherUI.show(another_frame, [4, 5, 6])
end)
AnotherUI.show(another_frame, [4, 5, 6])
Kino.nothing()

Expected solution

Kino already has API for streams via Kino.Control.stream/1. I would love to simplify the code above to something like:

load_btn = Kino.Control.button("Load data") |> Kino.render()
event = Kino.Control.event(:results)

load_btn 
|> Kino.listen(fn _event -> 
  # very heavy db query 
  results = [1, 2, 3]
  Kino.send(event, data: [results: results]) 
end)

# another cell

event
# or  [event, ...other events] |> Kino.Control.stream()
|> Kino.animate(fn event -> 
  Kino.Markdown.new("Some view #{Enum.join(event.data.results, ", ")} #{DateTime.utc_now()}")
end)

## another cell !

event
|> Kino.animate(fn event -> 
  Kino.Markdown.new("Different view #{Enum.join(event.data.results, ", ")} #{DateTime.utc_now()}")
end)

It's also very likely that I'm doing something wrong and this is already possible but given the validation here I don't think it's easy to do nor obvious from the documentation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions