TabaegraergaergenieFramework
In this article, we will look at creating a web application – an airport scoreboard featuring a table of flights. To familiarise yourself with the basic logic of web applications in GenieFramework, we recommend that you read the introduction article. Here we will focus on working with tables via DataTable, interacting with CSV files and dynamically updating data using an asynchronous loop. We will pay particular attention to why DataTable is used to display the table, rather than simply DataFrame.

Application Code
using GenieFramework
using Stipple
using Stipple.Tables
using DataFrames
using Dates
using CSV
function generate_flight_data()
DataFrame(
"Номер рейса"=>["SU123", "AF456", "BA789"],
"Назначение"=>["Москва", "Париж", "Лондон"],
"Время отправления" => [Dates.format(now() + Hour(rand(1:5)), "HH:MM") for _ in 1:3],
"Статус" => ["Вовремя", "Задержан", "Отменен"]
)
end
@app begin
@out current_time = "Текущее время: " * Dates.format(now(), "HH:MM:SS")
@out flight_table = DataTable(DataFrame()) # Пустая таблица изначально
@onchange isready begin
initial_df = generate_flight_data()
CSV.write("flight_data.csv", initial_df)
flight_table = DataTable(initial_df)
@async while true
sleep(5)
current_time = "Текущее время: " * Dates.format(now(), "HH:MM:SS")
df = CSV.read("flight_data.csv", DataFrame, types=String)
df.Status[rand(1:3)] = rand(["Вовремя", "Задержан", "Отменен"])
CSV.write("flight_data.csv", df)
flight_table = DataTable(df)
end
end
end
function ui()
[
h3("{{current_time}}"),
table(:flight_table; dense=true, flat=true)
]
end
@page("/", ui)
Programme logic: step-by-step analysis
Connecting packages
The following packages are used:
GenieFramework: The main framework for creating web applications.Stipple: Provides reactivity and communication between the server and the interface.Stipple.Tables: Provides aDataTables for displaying tables in the web interface.DataFrames,Dates,CSV: Required for working with tabular data, time formatting and file operations.
Each package plays its own role, providing a minimal set of tools for the application.
Data generation
The function generate_flight_data() creates a flight table:
- Returns an object
DataFramewith four columns:FlightNumber``: Flight numbers (“SU123”, “AF456”, “BA789”).Destination: Destinations (“Moscow”, “Paris”, “London”).DepartureTime: Departure time in “HH:MM” format, based on the current time plus a random offset (1–5 hours).Status: Flight statuses (“On Time”, “Delayed”, “Cancelled”).
- This function emulates the initial data for the scoreboard, which will then be updated whilst the application is running.
Data model: variables
The block ‘ @app ’ defines the data model:
@out current_time: A reactive variable containing a string with the current time (“Current time: HH:MM:SS”). It is initially set at startup and updated later.@out flight_table: A reactive type variableDataTable, initially empty (DataTable(DataFrame())). It will be populated with data when the application is loaded.
These variables are available for display in the interface and automatically update it when they change.
Data model: initialisation and updating
The handler @onchange isready controls the application logic:
isreadys and their roles:isready— this is Stipple’s built-in reactive variable, which initially has the valuefalse. It becomestruewhen the client (browser) has fully loaded the page and established a connection to the server via WebSocket.@onchange isreadymeans that the code inside the block is executed once whenisreadychanges fromfalsetotrue, that is, upon the first successful download of the application on the client side. This does not mean that updates are limited to this moment alone. —@onchangeit runs the code once, but we can initiate persistent processes within it.- We use
@onchange isreadyto ensure that data initialisation and the start of the update cycle occur only after the interface is ready for operation. Without this, the code could execute too early, before establishing communication with the client, which would lead to synchronisation errors.
- Initialisation:
- The initial table is created
initial_dfviagenerate_flight_data(). - Data is written to a file
flight_data.csv. flight_tableis updated withDataTable(initial_df).
- The initial table is created
- Asynchronous loop:
- Starts via
@async while true, which creates a background task.- Every 5 seconds (
sleep(5)):
- Every 5 seconds (
- Updates
current_time.- Data is read from the parameter file
types=Stringto save the string format. - The status of one flight is changed at random.
- The updated data is written to a file and assigned to
flight_table``.
Interface
- Data is read from the parameter file
The function ui() sets the appearance:
h3("{{current_time}}"): Displays the current time, which is updated automatically.table(:flight_table; dense=true, flat=true): Creates a table linked toflight_table``.dense=truemakes the table compact.flat=trueremoves shadows for a simple design.DataTableautomatically generates headings from column namesDataFrame.
Page Registration
@page("/", ui): Indicates that the interface is accessible via the root route "/". This completes the configuration of the application.
Why DataTable rather than simply DataFrame?
DataFrame — this is a powerful Julia tool for working with tabular data in code. It is excellent for data manipulation: filtering, sorting and calculations. However, on its own DataFrame It is not intended to be displayed in a web interface. It represents data in the programme’s memory, but it does not have built-in logic for transferring it to the browser or rendering it as an HTML table.
DataTable from Stipple.Tables solves this problem:
- Reactivity:
DataTableintegrates with the Stipple reactivity system. When the data inflight_tablechanges, the interface is updated automatically without the need to manually redraw the table. - Formatting for the web:
DataTableconvertsDataFrameinto a structure compatible with Quasar (the framework used by Stipple for the interface), adding headers and data in the desired format. - Simplicity: Using
table(:flight_table)allows you to display data without writing complex HTML or JavaScript code. If we were to use justDataFrame, I would have to manually convert it to an array of dictionaries (Vector{Dict}) and pass it to the interface, which complicates the code.
Therefore, DataFrame — this is the ‘raw’ data, and DataTable — a ‘bridge’ between them and the web interface, providing convenience and automation.
---### How do I launch the app?
Go to the folder containing the current script and run the cell below.
This should open a window displaying the web application.
If it does not open in a separate tab, click on the link in the output cell.
match(r"'(https?://[^']+)'"allows you to find a link based on the output of thegenie.startfunction using regular expressionsMarkdown.parsethis is used to display ‘clickable’ links in the output cell.
using Markdown
cd(@__DIR__)
app_url = string(engee.genie.start(string(@__DIR__,"/app.jl")))
Markdown.parse(match(r"'(https?://[^']+)'",app_url)[1])