Stipple.jl
The reactive UI library for interactive data applications with pure Julia.
Install / Use
/learn @GenieFramework/Stipple.jlREADME
Part of Genie Framework, Stipple is a reactive UI library for building interactive data applications in pure Julia. It uses Genie.jl on the server side and Vuejs on the client. Stipple uses a high performance architecture that automatically synchronizes the state two-way (server -> client and client -> server), sending only JSON data over the wire.
</div>Besides the low-code API provided by Stipple, you can also use Genie Builder to build your reactive UIs. Genie Builder is a plugin for VSCode to help you create interactive Genie apps much faster using its visual drag-and-drop editor.
To learn more about Stipple and Genie Framework, visit the documentation, and the app gallery.
If you need help with anything, you can find us on Discord.
The Stipple ecosystem also includes:
- StippleUI.jl - the UI library for
Stipple.jl, providing access to 30+ reactive UI elements of the Quasar Framework, including forms, lists, tables, as well as layout. - StipplePlotly.jl - Plotting library for
Stipple.jlbased on Plotly's Graphing Library including event forwarding for interactive plots. - StipplePlotlyExport.jl - add-on for
StipplePlotly.jlto allow server side generation and exporting of plots. - StippleLatex.jl - support for reactive Latex content based on the Vue-Katex plugin.
News (0.31.4 - v0.31.25):
- Support GenieBuilder with Vue 3
- Add
@fixtypeto prevent Revise from raising a world-age issue when revising app-mixins - Generate Vue Components from apps, see example below:
News (v0.31.3/0.31.4):
Features
Synchronization
- add functions
synchronize!(o1, o2)andunsynchronize!(o1, o2)to sync/unsync reactive variables to external Observables. Sync is bidirectional by default - Example below.
Finalizers
- add finalizers that strip off listeners when a model goes out of scope.
- custom finalizers can be added, e.g. to unsync model variables from external sources
News (v0.31):
Features
- referring to variables in the
@appdeclaration, e.g.@in y = x + 1 - multiple models on one page
- debugging of model events with
Stipple.debug() - add
throttlein analogy todebounce @handlermacro to define app handlers outside of@app- handler merging in mixins
- accept Inf and NaN in floats
- routehandlers
pre/postfor@page - precompilation by
@stipple_precompile
Fixes / Improvements
- reconnection after network interruption, e.g. energy save mode
@asynccalls in@onchangesections- remove state-changing evals from ReactiveTools macros
Vue 3 / Quasar 2
From version 0.30 on Stipple has upgraded the front-end libraries to Vue3 / Quasar 2, as Vue-2 has reached its end-of-life. We recommend to use at least v0.30.3, as we have fixed some relevant bugs. (Some of these bugs were also present in previous Vue2 versions. we therefore recommend to use at least v0.29.1 for legacy Vue2 support.)
We have put lots of effort in making migration as easy as possible. Nevertheless, there are some places where advanced apps might need a little tweeking.
Main Changes for version >= v0.30
First of all, basic applications continue to function properly without any changes.
However, if you have developed applications with external plugins or components you might need to do some adaptations. We have introduced a vue2compat layer to make the migration as easy as possible.
Components
- Components can no longer be registered in Vue, but rather in the app instance. If you are using legacy components, e.g. by including Vue libraries, you can verify whether they have been recognized by entering
vueLegacyin the browser console once the page has been loaded (right-click -> 'Inspect'). Under the field "components" you will find the components to be registered. Registering is then done by addingStipple.register_global_component("<component name>", legacy = true)to your app. - Vue-2 components often use
this.$set(this.field, key, value)to set object fields, which is incompatible with Vue-3 syntax. Use a normal assignment instead, e.g.field[key] = value
Plugins
If libraries install plugins, they need to be added to the app via Stipple.add_plugin(MyApp, "<plugin name>").
If you are using explicit models replace 'MyApp' by the model type, otherwise use the module's name.
Component Attributes
Vue-2 syntax for synchronizing additional fields other than the v-model has changed from <fieldname>.sync to v-model:<fieldname>, e.g. from :pagination.sync = "myvar to v-model:pagination = "myvar". In StippleUI we had offered "paginationsync = :myvar" which will be automatically translated to the new syntax. If want to explicitly enter the original form, you can do so by using the var"" syntax, " , e.g. table(:mytable, var"v-model:pagination" = "test")
Templates
In Vue-3 v-if and v-for precedence have change. So if you use both attributes in one component make sure to split your component in a container with a v-for attribute and a component, e.g. div or span with a v-if attribute.
In StippleUI syntax v-if corresponds to @if("...")and v-for corresponds to @for("...")
More Migration Support
... can be found at the Quasar site: https://quasar.dev/start/upgrade-guide/ and at the Vue site: https://v3-migration.vuejs.org/
Installation
Stipple can be added from the GitHub repo, via Pkg:
pkg> add Stipple
Example 1
This snippet below illustrates the structure of a reactive UI built with Stipple. See its documentation page for a detailed explanation.
module App
#setup of the Genie Framework environment
using GenieFramework
@genietools
# reactive code
@app begin
# reactive variables synchronised between server and browser
@in N = 0
@out msg = ""
#reactive handler, executes code when N changes
@onchange N begin
msg = "N = $N"
end
end
# UI components
function ui()
[
cell([
p("Enter a number")
# variables are bound to a component using their symbol name
textfield("N", :N )
])
cell([
bignumber("The value of N is", :N)
])
]
end
# definition of root route
@page("/", ui)
end
<img src="https://learn.genieframework.com/assets/docs/ui/enternumber.png" width="200px" style="margin-left:auto;margin-right:auto"> </img>
Example 2 (Synchronization to external sources)
using Stipple, Stipple.ReactiveTools
using StippleUI
using GenieSession
const XX = Dict{String, Reactive}()
@app Observer begin
@in x = 0
@private session = ""
@onchange isready begin
@info "session: $session"
println("hi")
r = get!(XX, session, R(x))
synchronize!(__model__.x, r)
end
end
@page("/", slider(1:100, :x), model = Observer, post = model -> begin model.session[] = session().id *""; nothing end)
@event Observer :finalize begin
println("unsynchronizing ...")
@info unsynchronize!(__model__.x)
notify(__model__, Val(:finalize))
end
@debounce Observer x 0
@throttle Observer x 10
up()
Example 3 (Generate a Vue Component for an animated Copy Button)
using Stipple, Stipple.ReactiveTools
using StippleUI
import Stipple.opts
# defining a simple copy button component
@app VueCopyButton begin
@out copied = false
end
@methods VueCopyButton [
:handleCopy => js"""function () {
const text = typeof this.text === 'function' ? this.text() : this.text;
Quasar.copyToClipboard(text).then(() => {
this.copied = true;
setTimeout(() => {
this.copied = false;
