Creating your first dashboard

A dashboard is a type of reactive app that displays data visualizations and allows the user to interact via controls such as buttons or sliders. With each user interaction, the application responds by executing code that manipulates the underlying data and promptly reflects these changes in the user interface. All of this happens in real time, without the need to reload the page.

This guide will show you how to create a dashboard which displays statistics about a vector of random numbers, and allows the user to change the length of the vector. The code will rely mainly on the reactive UI features implemented by the Stipple.jl package, which is already included in the GenieFramework.jl metapackage.

Project structure

When building a Genie app, the usual assumption is that you already have some code performing some data analysis that you want to turn into a web app. For this demo, we'll start with a StatisticAnalysis module with the following content:

StatisticAnalysis.jl
module StatisticAnalysis

export gen_numbers, calc_mean

function gen_numbers(N::Int)
    return rand(N)
end

function calc_mean(x::Vector{Float64})
    return round(sum(x) / length(x); digits=4)
end
end

Create a project folder for the demo app, and in it create a lib/ folder and place the StatisticAnalysis.jl file inside it. The contents of this folder will be automatically loaded into the Main module when the app is run.

Next, we have the app.jl file which is the main entry point to the app and also where we'll implement the dashboard. Create the file in the project root with this content:

app.jl
module App
using Main.StatisticAnalysis
using GenieFramework, PlotlyBase
@genietools

@app begin
    #reactive code goes here
end

function ui()
    p("") #initialized to an empty paragraph
end

@page("/", ui)
end

The code is divided into three parts:

  • Imports of GenieFramework, PlotlyBase and the data analysis code in StatisticAnalysis.jl.
  • Reactive code within the @app block to control dashboard interactivity.
  • UI definition in the ui function.
  • Route definition with @page to link a URL with the page view provided by ui.

This is what the file structure should look like:

├── app.jl
└── lib
    └── StatisticAnalysis.jl

To launch the app, run this in the Julia REPL:

using GenieFramework; Genie.loadapp(); up()

A server will start on port 8000, and you can access the app at https://localhost:8000. To stop the server, execute down() in the REPL.

Let's now add the UI code and reactive code to implement the following:

  • A slider to control the length of the vector of random numbers.
  • A text line to display the vector's mean.
  • A histogram plot for the vector.

Slider and mean - UI

You can build the UI in pure Julia using the low-code API provided by the StippleUI.jl package, which is automatically included upon using GenieFramework. This provides multiple calls such as textfield or slider that generate the HTML code for the UI component:

julia> slider(1:1:1000, :N)
"<q-slider :min=1 v-model=\"N\" :max=1000 :step=1></q-slider>"

See the component gallery and the API for a complete list of available components.

To incorporate the slider component and the mean display into the UI, add the following code to the ui function:

app.jl
function ui()
    row([
    cell(class="st-col col-3", [
        h1("A simple dashboard"),
        slider(1:1:1000, :N),
        p("The average of {{N}} random numbers is {{m}}", class="st-module"),
        ])
    ])
end
A simple dashboard

The average of {{N}} random numbers is {{m}}

The slider component takes as parameters the range of values it takes, and the symbol name of the reactive variable where the selected number will be stored. This variable does not exist yet. We'll add it later to the reactive code.

The {{variable}} syntax is used in the paragraph p to display the content of reactive variables. Moreover, this syntax accepts any valid Javascript expression, so you can write things like {{m.toFixed(2)}} to display the mean with two decimals.

You can examine the HTML generated by the ui function by calling it in the REPL, which will yield:

<div class="row">
   <div class="st-col col-3 st-col col">
      <h1>A simple dashboard</h1>
      <q-slider :min=1 v-model="N" :max=1000 :step=1 ></q-slider>
      <p class="st-module">The average of {{N}} random numbers is {{m}}</p>
   </div>
</div>

Since the N, m variables in the code are just placeholders for now, the UI won't work. Let's add the reactive code to make it interactive.

Slider and mean - logic

When the slider is moved, we want to set a new value for the vector length and show the updated mean. To this end, first declare a reactive variable N in the @app block as:

app.jl
@app begin
   @in N = 0
end

Tagging with @in makes the variable reactive and writeable from the UI. Whenever its value changes in the UI in the browser, the change will be automatically propagated to the Julia code. Likewise, any change in the Julia code will be propagated to the UI.

Next, add a read-only output variable to hold the value of the mean:

app.jl
@out m = 0.0

Variables tagged with @out can only be modified from the Julia code. Any change made in the browser will not be propagated to the backend. Note that reactive variables must always be initialized to a value of the appropriate type.

In addition, you can use the @private macro to define non-reactive variables that are not exposed to the UI. Use these to store data not meant to be shared between users.

Finally, use the @onchange macro to declare a reactive handler that generates a new vector and calculates its mean when N changes:

app.jl
@app begin
    @in N = 0
    @out m = 0.0
    @onchange N begin
        x = gen_numbers(N)
        m = calc_mean(x)
    end
end

Using the @in, @out and @onchange you can implement all kinds of interactivity in an easy manner. To learn more, check out the Reactivity reference.

You should now have a working reactive UI! Let's add the histogram plot.

Histogram plot

To display a plot in the UI, add the plot call as:

app.jl
row([
    cell(class="st-col col-3", [
        h1("A simple dashboard"),
        slider(1:1000, :N),
        p("The average of {{N}} random numbers is {{m}}", class="st-module"),
        plot(:trace, layout=:layout)
    ])
])

This will add a Plotly plot, in which the trace variable will hold the data to be plotted, whereas layout determines style options.

Now, declare an empty histogram trace and the layout variable as

app.jl
@app begin
    @out trace = [histogram(x=[])]
    @out layout = PlotlyBase.Layout(title="Histogram plot")
    . . .
end

Then, add the code to update the trace in the reactive handler when N changes

app.jl
@onchange N begin
    . . .
    trace = [histogram(x=x)]
end

This is the final reactive code in app.jl:

app.jl
@app begin
    @in N = 0
    @out m = 0.0
    @out trace = []
    @out layout = PlotlyBase.Layout(title="Histogram plot")
    @onchange N begin
        x = gen_numbers(N)
        m = calc_mean(x)
        trace = [histogram(x=x)]
    end
end

And that's it, you've finished your first dashboard! For more details on plotting, see the Plotting reference page. Then, you can continue reading the guides to add more features such as multiple pages or an API.


Genie