Adding dynamic pages
Modern web applications are often characterized by their ability to generate content dynamically, tailoring web pages based on user interactions, database queries, or other factors. This guide provides an overview of how to create web pages with dynamic content.
Dynamic content refers to the parts of a web page that change based on varying factors such as user input, database changes, or real-time data. Instead of serving static HTML files, the server generates the content of the page on-the-fly, often pulling from databases or other data sources.
Pages in a route
For simple pages displaying short messages or static content, you can add the page directly in the route definition with the do
end
syntax:
using Genie
route("/message") do
$name = "John"
h1("Welcome to Genie $name !")
end
Note that we are using Genie
and not GenieFramework
as we don't need the reactive features provided by the Stipple
package.
Defining pages in a file
You can define the page's code in a file and include it as
route("/message") do
name = "John"
html(Genie.Renderer.filepath("message.jl"), name=name)
end
h1("Welcome to Genie $name !")
The html
function renders the code and adds information to the HTTP response header.
You can also use HTML by directly replacing the Julia code and files with HTML strings and files.
using Genie.Genie.Renderer.Html
route("/message") do
html(Renderer.filepath("pages/message.jl.html"))
end
<h1>Welcome to Genie $name !</h1>
Executing Julia code in a page
It is possible to embed Julia code in a page's code that will be executed when the page is loaded. To do so, use the $
interpolation operator to wrap Julia code as
p("1+1 is: $(1+1)")
<p>1+1 is: $(1+1)</p>
To include multiline code blocks, use the <%
and %>
delimiters as
<p>
<% for i in 1:3 %>
$i
<% end %>
</p>
Remember to always pass any variable to be printed to the html
renderer.
Including static assets
To displayed static assets such as images, or include assets in the head of a page, place them first in the public
folder. They will be automatically picked up by the server and served at the root path /
. Then, the assets can be included in any page like in the example below.
.
├── app.jl
├── public/
│ ├── style.css
│ ├── meta.png
│ └── fig.png
function assets()
[
head([
meta(charset="utf-8")
meta(name="og:image", content="/meta.png")
link(rel="stylesheet", href=("/style.css"))
title("Including assets")
])
body([
img(src="/fig.png",alt="plot")
])
]
end
route("/assets") do
html(assets())
end
Also in HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="og:image" content="/meta.png" />
<link rel="stylesheet" href="/style.css" />
<title>Including assets</title>
</head>
<body>
<img src="/fig.png" alt="Meta" />
</body>
</html>
Using layouts
A layout definition is useful to set a common structure across pages, such as when adding a navigation bar. You can define layouts and pass them to the html
function as
function layout()
[head(title("Genie app")),
body([h1("Welcome!"), @yield])]
end
route("/layout") do
html("layout test", layout=layout, status=200)
end
Here, @yield
injects the page's content into the layout.
Pages with a controller
When pages grow in complexity, it can be useful to separate the logic from the view and define a controller. This allows you to keep the code organized and the logic reusable.
flowchart LR
subgraph Router
a1["/message"]
end
subgraph View
v1["message.jl.html"]
end
subgraph Controller
c1["message()"]
end
a1 --- c1
c1 --- v1
Following the model-view-controller (MVC) architecture, your app's files should be organized like this
.
├── app.jl
├── controllers/
└── pages/
where app.jl
is the app's entry point.
module App
using Genie
Genie.Loader.autoload("controllers")
#route definitions go here
end
To add a page, create a new controller in controllers
with a handler function that will perform any calculations or data analysis necessary and return the rendered page. In the call to the html
renderer, pass as parameters the variables to be displayed in the page.
module MessageController
using Genie.Genie.Renderer.Html
using Dates
function message()
current = Dates.now()
tomorrow = current + Dates.Day(1)
html(Renderer.filepath("pages/message.jl"), current=current, tomorrow=tomorrow)
end
end
Then, add the page's code.
p("Today's date and time: $current")
p("Tomorrow's date and time: $tomorrow)
The last step is adding a route in app.jl
linking the page's path to its controller function.
module App
using Genie
Genie.Loader.autoload("controllers")
using .MessageController
route("/message", MessageController.message)
end