Stipple.jl
Stipple.jl copied to clipboard
Displaying table with columns of type different from String
A Genie Builder user reported this issue:
We found a bug with the table display. Basically, whenever we send a table with column values that JSON3 can't directly handle, the display isn't shown. For example, the attached MWE has a table with units from Unitful.jl and it is not. The fix consists of converting all entries of the table to string first with string.(dataframe) before sending to the Quasar UI components.
module App
# set up Genie development environmet
using GenieFramework
using DataFrames
using Unitful
@genietools
# add your data analysis code
function mean(x)
sum(x) / length(x)
end
# add reactive code to make the UI interactive
@app begin
# reactive variables are tagged with @in and @out
@in N = 0
@out msg = "The average is 0."
# @private defines a non-reactive variable
@private result = 0.0
@out table = DataTable(DataFrame(a = rand(5) * u"m", b = rand(5) * u"s"))
# watch a variable and execute a block of code when
# its value changes
@onchange N begin
# the values of result and msg in the UI will
# be automatically updated
result = mean(rand(N))
msg = "The average is $result."
table = DataTable(DataFrame(a = rand(5) * u"m", b = rand(5) * u"s"))
end
end
# register a new route and the page that will begin
# loaded on access
@page("/", "app.jl.html")
end
<h4> Hello Genie! </h4>
<p> Generated {{N}} random numbers. </p>
<p v-text="msg"></p>
<q-slider v-model="N" :label="true"></q-slider>
<p>This is the default view for the application.</p>
<p>You can change this view by editing the file <code>app.jl.html</code>.</p>
<q-table :data="table.data"></q-table>
You could define your own render function for unitful types, e.g.:
function Stipple.render(x::Union{Unitful.AbstractQuantity, Unitful.Unitlike})
io = IOBuffer()
show(io, x)
String(take!(io))
end
Stipple.render(x::Array{<:Union{AbstractQuantity, Unitful.Unitlike}, N}) where N = Stipple.render.(x)
and add recursive rendering for Dicts
Stipple.render(d::AbstractDict) = OrderedDict(keys(d) .=> render.(values(d)))
Stipple.render(d::Array{<:AbstractDict, N}) where N = render.(d)
function Stipple.render(t::T) where {T<:DataTable}
render(StippleUI.Tables.data(t))
end
then
julia> dt = DataTable(DataFrame(a = rand(5) * u"m", b = rand(5) * u"s"));
julia> render(dt)
OrderedDict{String, Vector} with 2 entries:
"data" => OrderedDict{String, Any}[OrderedDict("__id"=>1, "b"=>"0.8485991568795399 s", "a"=>"0.4107245790663422 m"), …
"columns" => Column[Column("a", false, "a", :left, "a", true), Column("b", false, "b", :left, "b", true)]
@essenciary We might consider recursive rendering for Dicts and Vectors in general?
Thought a bit about my last sentence. General recursive rendering in Vectors is certainly not a good idea, as it would largely decrease the performance when rendering vectors of numbers. But for special types, e.g. DataTables that would be a good idea. Still not sure what the best approach would be.