Working with forms
This guide will show you how to create a simple app in which the user can enter a value and trigger a computation, with the result being displayed afterwards.
Then, we'll turn the above code into a web app, the app where the user will enter a value for N
, and the mean will be displayed as in this preview:
![](/.netlify/images?url=%2Fassets%2Fdocs%2Fguides%2Ffirst-genie-app%2Fpreview.gif)
Moreover, we'll add a reactive page where the analysis is triggered when the user types a value in the text field:
![](/.netlify/images?url=%2Fassets%2Fdocs%2Fguides%2Ffirst-genie-app%2Freactive.gif)
Running the app
For this demo Genie app, we'll start with a StatisticAnalysis
module with functions to generate a vector of random numbers and calculate its mean:
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
Start with an empty project folder and create a lib/
folder in it. Then, place the StatisticAnalysis.jl
file inside lib/
. The contents of this folder will be automatically loaded into the Main
module when the app is run.
Next, create a file app.jl
, which will be the entry point to the app, with the following content:
module App
using GenieFramework
using Main.StatisticAnalysis
@genietools
end
The GenieFramework
package includes everything you'll need to build the web app, and the @genietools
macro enables features such as logging and hot code reloading.
This is what the file structure should look like so far:
.
├── app.jl
└── lib/
└── StatisticAnalysis.jl
To launch the app, start a Julia REPL with julia --project
in the project folder and run
using GenieFramework; Genie.loadapp(); up()
This will start a server on port 8000
and you can access the app at http://localhost:8000. To stop the server, execute down()
in the REPL.
Now that you've set up the computational code and the app is running, it's time to add the pages to trigger the analysis and display the results.
Welcome page
Let's start with a simple index page at the root /
path with a link to the number input form. You can create it with a route, which takes a as an argument an action function that returns the content of the page:
module App
using GenieFramework
using Main.StatisticAnalysis
route("/") do
[ h4("Welcome to the number analysis module"),
a("Numbers form", href="/form") ]
end
end
The h4
and a
functions come from Genie's low-code UI API, in which each function generates the corresponding HTML element. This allows you to implement pages in pure Julia. When you run the app, you should see the page at http://localhost:8000
.
Number input form page
The second page will have a form with an input field to introduce the length of the vector of random numbers, with a submit button to send the information to the server. For this page, create a route /form
the following content in its action function:
route("/form") do
Html.form(action = "/result", method="POST", [
input(type="number", name="N", placeholder="Enter vector length")
input(type="submit", value="Send")
])
end
The action
parameter in the form
element specifies the path to which the form data will be sent, and method
specifies the HTTP method. We'll use POST
to send the data enclosed in the body of the HTTP request. An alternative would be GET
, with the data being appended to the URL as /result?N=[value]
.
Inside the form, the number
type input will show as an input box, whereas the submit
type input will appear as the button that submits the form.
Results page
When the form is submitted, a POST
request will be sent to the /result
path, where the analysis will be performed with the form data. For the results page, define a route implementing the processing as
using GenieFramework.Genie.Requests: postpayload
route("/result", method=POST) do
N = parse(Int, postpayload(:N))
x = gen_numbers(N)
m = calc_mean(x)
p("The mean of $N random numbers is: $m", style="font-size:20px")
end
The postpayload
call from the Requests
module extracts the value of N
from the header of the POST
request submitted by the form. Then, the analysis is performed and a response with the result is rendered. The usual Julia string interpolation syntax is used to display the value of N
and m
.
Reactive form page
In some applications like dashboards, you might want to update parts of the UI when the user interacts with a component. When these updates happen without a page reload, this is known as reactivity. Genie Framework provides an elegant API to implement reactive APPs with two-way instant synchronization between the backend and the page. To introduce it, we'll reimplement the number analysis form.
Create a new module ReactiveForm.jl
with the following content:
module ReactiveForm
using GenieFramework
using .Main.App.StatisticAnalysis
@genietools
@app begin
@in N = 0
@out m = 0.0
@onchange N begin
m = calc_mean(gen_numbers(N))
end
end
function ui()
cell([
textfield("How many numbers?", :N),
p("The average of {{N}} random numbers is {{m}}"),
])
end
@page("/reactive", ui)
end
Then, include the module in app.jl
by appending include("ReactiveForm.jl")
to the end of the file as
module App
using GenieFramework
using Main.StatisticAnalysis
# . . .
include("Reactiveform.jl")
In the flow implemented by ReactiveForm.jl
, when the user types a number in the text field a reactive variable N
is updated with the new value. This update triggers a handler that recalculates the mean and stores it in a reactive variable m
, whose value will be automatically sent to the UI. As you can see in the gif below, all of this happens without a page reload.
![](/.netlify/images?url=%2Fassets%2Fdocs%2Fguides%2Ffirst-genie-app%2Freactive.gif)
Now, let's break down the code into its parts:
- Imports: besides importing
GenieFramework
we also import the data analysis code fromStatisticAnalysis
. This code could also have been defined in the module itself. - Reactive code: the block delimited by the
@app
macro implements the reactive code, which controls the app's interaction with the user. - Reactive variables: the variables tagged with
@in
and@out
monitor their own value so that when it changes, the changes are automatically propagated to the UI. The@in
macro specifies a variable that can be modified from the UI, whereas@out
defines a read-only variable only modifiable from the Julia code.
@in N = 0
@out m = 0.0
- Reactive handler: the
@onchange
macro defines a handler that is executed when the value of the tagged variable changes.
@onchange N begin
m = calc_mean(gen_numbers(N))
end
- UI function: as seen previously, the UI is implemented in a
ui
function with the low-code API. Thetextfield
is bound to theN
variable, which will store the component's state. Moreover, the{{}}
syntax is used to display a reactive variable's value in a page.
function ui()
cell([
textfield("How many numbers?", :N),
p("The average of {{N}} random numbers is {{m}}"),
])
end
- Route: the
@page
macro defines a route that will render the specifiedui
function.
@page("/reactive", ui)