Stipple is a reactive UI library for building interactive data applications in pure Julia. It uses Genie.jl (on the server side) and Vue.js (on the client).
Stipple uses a high performance MVVM architecture, which automatically synchronizes the state two-way (server -> client and client -> server) sending only JSON data over the wire.
The Stipple package provides the fundamental communication layer, extending Genie
's HTML API with a reactive component.
The Stipple ecosystem also includes:
- StippleUI.jl - the UI library for
Stipple.jl
, providing access to 30+ reactive UI elements (including forms, lists, tables, as well as layout). - StippleCharts.jl - the charts library for
Stipple.jl
, providing access to a growing collection of reactive charts.
Stipple can be added from the GitHub repo, via Pkg
:
pkg> add Stipple
Downloadable demos repo available at: https://github.com/GenieFramework/StippleDemos
Add Genie
and Stipple
first:
pkg> add Stipple
pkg> add Genie
Now we can run the following code at the Julia REPL:
using Genie, Genie.Renderer.Html, Stipple
Base.@kwdef mutable struct Name <: ReactiveModel
name::R{String} = "World!"
end
model = Stipple.init(Name())
function ui()
page(
vm(model), class="container", [
h1([
"Hello "
span("", @text(:name))
])
p([
"What is your name? "
input("", placeholder="Type your name", @bind(:name))
])
]
) |> html
end
route("/", ui)
up() # or `up(open_browser = true)` to automatically open a browser window/tab when launching the app
This will start a web app on port 8000 and we'll be able to access it in the browser at http://localhost:8000
Once the page is loaded, we'll be able to interact with the data and see how it's synced.
We can update the name value from Julia, and see it reflected on the page, at the REPL:
julia> model.name[] = "Adrian" # updating the property in Julia will update the values on the front
On the webpage, we can change the name in the input field and confirm that it has been updated in Julia:
julia> model.name[] # will have the same value as what you have inputted on the web page
You can see a quick video demo here: https://www.dropbox.com/s/50t5bqd2zk4ehxo/basic_stipple_3.mp4?dl=0
The Stipple presentation from JuliaCon 2020 is available here (8 minutes): https://www.dropbox.com/s/6atyctgomsqwjki/stipple_exported.mp4?dl=0
This snippet illustrates how to build a UI where the button triggers a computation (function call) on the server side, using the input provided by the user, and outputting the result of the computation back to the user.
using Genie, Genie.Renderer.Html, Stipple
Base.@kwdef mutable struct Model <: ReactiveModel
process::R{Bool} = false
output::R{String} = ""
input::R{String} = ""
end
model = Stipple.init(Model())
on(model.process) do _
if (model.process[])
model.output[] = model.input[] |> reverse
model.process[] = false
end
end
function ui()
page(
vm(model), class="container", [
p([
"Input "
input("", @bind(:input), @on("keyup.enter", "process = true"))
])
p([
button("Action!", @click("process = true"))
])
p([
"Output: "
span("", @text(:output))
])
]
) |> html
end
route("/", ui)
up()
By default Stipple will attempt to use WebSockets for real time data sync between backend and frontend.
However, in some cases WebSockets support might not be available on the host. In this case, Stipple can be
switched to use regular HTTP for data sync, using frontend polling with AJAX (1s polling interval by default).
Stipple can be configured to use AJAX/HTTP by passing the transport
param to the init()
method, ex:
model = Stipple.init(Name(), transport = Genie.WebThreads)
The current available options for transport
are Genie.WebChannels
(default, using WebSockets) and
Genie.WebThreads
(using HTTP/AJAX).
Given that polling generates quite a number of extra requests, it can be desirable to disable automatic
logging of requests. This can be done using Genie.config.log_requests = false
.
Support for WebThreads
and request logging disabling has been introduced in Genie v1.14 and Stipple v0.8.
using Genie, Genie.Renderer.Html, Stipple
Genie.config.log_requests = false
Base.@kwdef mutable struct Name <: ReactiveModel
name::R{String} = "World!"
end
model = Stipple.init(Name(), transport = Genie.WebThreads)
function ui()
page(
vm(model), class="container",
[
h1([
"Hello "
span("", @text(:name))
])
p([
"What is your name? "
input("", placeholder="Type your name", @bind(:name))
])
]
) |> html
end
route("/", ui)
up()
The full application is available at: https://github.com/GenieFramework/Stipple-Demo-GermanCredits
The full application is available at: https://github.com/GenieFramework/Stipple-Demo-IrisClustering