Table of Contents
Basolato use nim-templates
as a default template engin. It can be used by importing basolato/view
.
import basolato/view
proc baseImpl(content:string): string = tmpli html"""
<html>
<heade>
<title>Basolato</title>
</head>
<body>
$(content.get)
</body>
</html>
"""
proc indexImpl(message:string): string = tmpli html"""
<p>$(message.get)</p>
"""
proc indexView*(message:string): string =
baseImpl(indexImpl(message))
To prevent XSS, you have to use get
proc for valiable. It applies xmlEncode inside.
proc get*(val:JsonNode):string =
proc get*(val:string):string =
title = "This is title<script>alert("aaa")</script>"
params = @["<script>alert("aaa")</script>", "b"].parseJson()
import basolato/view
proc impl(title:string, params:JsonNode):Future[string] {.async.} = tmpli html"""
<h1>$(title.get)</h1>
<ul>
$for param in params {
<li>$(param.get)</li>
}
</ul>
Basolato view is designed for component oriented design like React and Vue.
Component is a single chunk of html and css, and just a procedure that return html string.
controller
import basolato/controller
proc withStylePage*(request:Request, params:Params):Future[Response] {.async.} =
return render(withStyleView())
view
import basolato/view
style "css", style:"""
.background {
height: 200px;
width: 200px;
background-color: blue;
}
.background:hover {
background-color: green;
}
"""
proc impl():string = tmpli html"""
$(style)
<div class="$(style.get("background"))"></div>
"""
proc withStyleView*():string =
let title = "Title"
return applicationView(title, impl())
This is compiled to html like this.
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<style type="text/css">
.background_jtshlgnucx {
height: 200px;
width: 200px;
background-color: blue;
}
.background_jtshlgnucx:hover {
background-color: green;
}
</style>
<div class="background_jtshlgnucx"></div>
</body>
</html>
style
template is a useful type for creating per-component style inspired by CSS-in-JS
.
Styled class names have a random suffix per component, so multiple components can have the same class name.
You can also use SCSS by installing libsass.
apt install libsass-dev
# or
apk add --no-cache libsass-dev
Then you can write style block like this.
style "scss", style:"""
.background {
height: 200px;
width: 200px;
background-color: blue;
&:hover {
background-color: green;
}
}
style
template crate Css
type instance in name
arg variable.
template style*(typ:string, name, body: untyped):untyped =
func `$`*(self:Css):string =
func get*(self:Css, name:string):string =
To send POST request from form
, you have to set csrf token
. You can use helper function from basolato/view
import basolato/view
proc index*():string = tmpli html"""
<form>
$(csrfToken())
<input type="text", name="name">
</form>
"""
If the user's input value is invalid and you want to back the input page and display the previously entered value, you can use old
helper function.
API
proc old*(params:JsonNode, key:string):string =
proc old*(params:TableRef, key:string):string =
controller
# get access
proc signinPage*(request:Request, params:Params):Future[Response] {.async.} =
return render(signinView())
# post access
proc signin*(request:Request, params:Params):Future[Response] {.async.} =
let email = params.getStr("email")
try
...
except:
return render(Http422, signinView(%params))
view
proc impl(params=newJObject()):string = tmpli html"""
<input type="text" name="email" value="$(old(params, "email"))">
<input type="text" name="password">
"""
proc signinView*(params=newJObject()):string =
let title = "SignIn"
return self.applicationView(title, impl(params))
It display value if params
has key email
, otherwise display empty string.
Other libraries which generate HTML can be chosen for Basolato too. However, each library has it's own benefits and drawbacks.
Library | Benefits | Drawbacks |
---|---|---|
htmlgen |
|
|
SCF |
|
|
Karax |
|
|
nim-templates |
|
|
Views file should be in app/http/views
dir.
Controller and result is same for each example.
controller
proc index*(): Response =
let message = "Basolato"
return render(indexView(message))
result
<html>
<head>
<title>Basolato</title>
</head>
<body>
<p>Basolato</p>
</body>
</html>
import htmlgen
proc baseImpl(content:string): string =
html(
head(
title("Basolato")
),
body(content)
)
proc indexImpl(message:string): string =
p(message)
proc indexView*(message:string): string =
baseImpl(indexImpl(message))
SCF should divide procs for each file
baseImpl.nim
#? stdtmpl | standard
#proc baseImpl*(content:string): string =
<html>
<heade>
<title>Basolato</title>
</head>
<body>
$content
</body>
</html>
indexImpl.nim
#? stdtmpl | standard
#proc indexImpl*(message:string): string =
<p>$message</p>
index_view.nim
#? stdtmpl | standard
#import baseImpl
#import indexImpl
#proc indexView*(message:string): string =
${baseImpl(indexImpl(message))}
This usage is Server Side HTML Rendering
import karax / [karasdsl, vdom]
proc baseImpl(content:string): string =
var vnode = buildView(html):
head:
title: text("Basolato")
body: text(content)
return $vnode
proc indexImpl(message:string): string =
var vnode = buildView(p):
text(message)
return $vnode
proc indexView*(message:string): string =
baseImpl(indexImpl(message))