-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for references inside reactive values #219
Comments
What doesn't work with the first example? var1 is not updated? |
The first example does not work as you get "ERROR: LoadError: UndefVarError: |
This requires some deep digging, but I think it's more complicated than this. First question is, how is var2 serialized to be sent to the client? And what does the data payload coming from the client look like? If it's all values, we need to change the serialization to actually use the variable in JS - and send back a symbol for that variable in Julia. Then we need to extend the the serialization/deserialization logic to support variables on both server and client. A potential issue being that at the point where var2 is deserialized on the server, var1 might not be in scope (can't remember). If I'm making myself clear: var2 is serialized as just data. When it comes back from JS as data/values, how do I know that var2[2] was actually a reference to a var (what var, that data is lost -- and is this var in scope)? That information is lost. |
Using variables/references in serialized data payloads goes against the core principles of Stipple: only data is sent over the wire. Julia objects are converted to their JS counterpart using their type's |
You would need to expand the macro to see what is generated. All the reactive vars are actually kept in the reactive model object that looks like However, even if that works, see above: serializing/deserializing references would not work: |
It gets complicated fast. See this: using GenieFramework
@genietools
mutable struct W{T}
value::T
end
Stipple.render(w::W) = w.value
Stipple.stipple_parse(::Type{W}, i::Int) = W(i)
@app begin
@in var1 = W(2)
@in var2 = [1,W(2),3]
@onchange var1 begin
@show var1, var2
end
end
ui() = slider(1:1:10, :var1) * textfield("", :var1) * textfield("", :var2)
@page("/", ui) Results: I remember talking to @hhaensel about deep serialization of collections, I think we decided it would be a performance issue. However, now I think we could do it by introducing a supertype of Array, ex |
I'm still reading through your replies but just to answer why I need this. This came up when in the exploratory data analysis app I had a single data source for multiple plots. After changing the data, I had to manually update each plot. It'd be nice if I could just change the data and all dependent variables would be updated. This is what Pluto does if I'm not mistaken. Example, having something like this work: @app begin
@in N = 100
@out data = randn(100)
@out hist = [histogram(x=data)]
@out sct = [scatter(x=data, y=data)]
@onchange N begin
data = randn(N)
end
end
ui() = [slider(1:1:10, :var1), plotly(:hist), plotly(sct)]
Now I have to update the plots inside the handler using GenieFramework, PlotlyBase
@genietools
@app begin
@in N = 100
@out data = []
@out hist = []
@out sct = []
@onchange isready, N begin
data = randn(N)
hist = [histogram(x=data)]
sct = [scatter(x=data, y=data)]
end
end
ui() = [slider(1:1:10, :N), plot(:hist), plot(:sct)]
@page("/", ui) Imagine having multiple filters for the data this would get repetitive. Although in this case you could define a handler for updating the plots and trigger it from other handlers, like @onchange N begin
data = randn(N)
notify(update)
end
@onchange update begin
#update plots
end |
Yes, sorry, I kind of digressed while digging into it. I do understand the use case and that could work if the container with the reference is not a reactive var itself or if it's just @out. If it is a reactive var whose value is overwritten from the client with serialized data, then things get very complicated, cause serialization is pure data, and does not include references. In addition, the references would have to be objects to be referenced, as value types are passed by value (copied). It could be as simple as a And also we need to solve the initial issue you reported, with the var not being found. |
Here is a small example that shows that references would work if we solve the various issues: ref = Ref{Int}(0)
@app begin
@in var1 = 1
var2 = [ref, 1,2]
@onchange var1 begin
ref[] = var1
@show var2
end
end
ui() = slider(1:1:10, :var1) * textfield("", :var1) Output:
When we update the slider, the ref gets updated as expected. |
I'd probably rather re-look at the task you are going to achieve. If you want to share the memory of data for different plots, why not setting up js-expressions that share the memory on the client side? @app begin
@in N = 100
@out data = []
@out hist = []
@out sct = []
@onchange isready, N begin
data = randn(N)
# hist = [histogram(x=data)]
# sct = [scatter(x=1:N, y=data)]
end
end
# fix an issue in rendering JSONText in plots
# (we should be fixing this in a future release of StipplePlotly ...)
import StipplePlotly.Charts.jsonrender
jsonrender(plot::GenericTrace) = jsonrender(Dict(plot))
jsonrender(plots::Vector{<:GenericTrace}) = jsonrender(Dict.(plots))
# render the trace expressions based on data, `js"data"` is equivalent to `JSONText("data")`
hist = [histogram(x=js"data")] |> jsonrender
sct = [scatter(x=js"Array.from({length: data.length}, (x, i) => i)", y=js"data")] |> jsonrender
ui() = [slider(1:100, :N), plot(hist), plot(sct)]
@page("/", ui)
up() |
Instead of writing the somehow ugly js array generator, I could as well have included a field @app begin
@in N = 100
@out ydata = []
@out xdata = []
@onchange isready, N begin
xdata = collect(1:N)
data = randn(N)
end
end
hist = [histogram(x=js"ydata")] |> jsonrender
sct = [scatter(x=js"xdata", y=js"ydata")] |> jsonrender |
Just patched StipplePlotly so that the above snippet runs without patching jsonrender. |
Thanks for the workaround @hhaensel, now any time the data variables change the plots are updated. Still, I'm not sure how this would work for something that is not a plot. Let's say I'd like to have this code work: using GenieFramework
@genietools
@app begin
@in var1 = 1
@in var2 = [var1,2,3]
@onchange var1 begin
@show var1, var2
end
end
ui() = slider(1:1:10, :var1) * "{{var2}}"
@page("/", ui)
up()
It doesn't work as using GenieFramework
@genietools
@app begin
@in var1 = 1
@onchange var1 begin
@show var1, var2
end
end
ui() = slider(1:1:10, :var1) * "{{[var1, 2, 3]}}"
@page("/", ui)
up()
which is not ideal. Also, to avoid confusion, I'd rather have people define as few vars outside I started this discussion to ask whether this was doable, but it seems like a difficult task also from what Adrian said. |
I digged a bit and I found out, that it is actually doable. The only thing you have to take care of is putting square brackets after the variable name: @app begin
@in var1 = 11
@in var2 = [var1[], 2, 3]
@onchange var1 begin
@show var1, var2
end
end
model = @init
model.var1[], model.var2[]
# (11, [11, 2, 3]) This also has some (negative?) consequences if you want to use a variable for initialisation with the same name as the model. var1 = 11
@app begin
@in var1 = 2 * var1
@in var2 = [var1, 2, 3]
@onchange var1 begin
@show var1, var2
end
end Here you overwrite |
If you use a different name for model variable and init value, then you are also safe: var1_init = 11
@app begin
@in var1 = 2 * var1_init
@in var2 = [var1_init, 2, 3]
@onchange var1 begin
@show var1, var2
end
end |
Do we really need a more sophisticated API? One last thing, if you don't want to waste your name space, you can define your init_value within the macro: @app begin
var1_init = 12
@in var1 = 2 * var1_init
@in var2 = [var1_init, 2, 3]
@onchange var1 begin
@show var1, var2
end
end
model = @init
model.var1[], model.var2[]
# (24, [12, 2, 3]) |
I need to add on this, that the above solution is not a permanent connection between the variables. But again, if you would like to bind the array julia> using Stipple, StippleUI
julia> select(:selector, options = R"[var1, 2, 3]")
"<q-select v-model=\"selector\" :options=\"[var1, 2, 3]\"></q-select>" because bindings are always evaluated as js-expressions in the context of the model. |
One more more thing. I am not sure what your main goal in this discussion is.
In the past I had developed a mechanism to update elements of vectors. This died in a very unfortunate way, but we could revive the topic if there is a need. That would be a way of lowering the data transmission footprint if you are handling large amounts of data with a high single-element update-rate, e.g. online data acquisition. |
I'd like to be able to use a reactive variable in another's definition, and have them linked. Something like this, where the handler for
var2
is triggered whenvar1
changes.Currently, one way to implement this would be
This would entail modifying the way
@app
instantiates the variables. It could also add a performance penalty, as we'd have to do a search for dependencies between variables.Alternatively, we could have a
@notify
macro that gets the model and callsnotify
The text was updated successfully, but these errors were encountered: