目次
Basolatoはオリジナルのテンプレートエンジンを持っています。これは basolato/view
をインポートすることで利用できます。
tmpl
マクロはComponent
オブジェクトを返します。
import basolato/view
proc baseImpl(content:Component): Component =
tmpl"""
<html>
<heade>
<title>Basolato</title>
</head>
<body>
$(content)
</body>
</html>
"""
proc indexImpl(message:string): Component =
tmpl"""
<p>$(message)</p>
"""
proc indexView*(message:string): string =
$baseImpl(indexImpl(message))
$()
を使うことで変数を呼び出すことができます
proc view():Component =
let msg = "Hello"
tmpl"""
<p>$(msg)</p>
"""
$(msg|raw)
を使うことで、エスケープしないで変数を表示することができます。
proc view():Component =
let msg = "<p>Hello</p>"
tmpl"""
<div>$(msg|raw)</div>
"""
$()
は関数も呼ぶことができます
proc msg():string =
return "Hello"
proc view():Component =
tmpl"""
<p>$(msg())</p>
"""
${}
の中に書いたNimのコードを書くことができます
proc view():Component =
tmpl"""
${ let msg = "Hello" }
<p>$(msg)</p>
"""
proc indexView(arg:string):Component =
tmpl"""
$if arg == "a"{
<p>A</p>
}
$elif arg == "b"{
<p>B</p>
}
$else{
<p>C</p>
}
"""
proc indexView(args:openarray[string]):Component =
tmpl"""
<li>
$for row in args{
<ul>$(row)</ul>
}
</li>
"""
proc indexView(args:openarray[string]):Component =
tmpl"""
<ul>
${ var y = 0 }
$while y < 4 {
<li>$(y)</li>
${ inc(y) }
}
</ul>
"""
Basolato viewは、ReactやVueのようなコンポーネント指向のデザインを採用しています。
コンポーネントとは、htmlとJavaScriptとCSSの単一の塊であり、htmlの文字列を返す関数のことです。
Basolatoではコンポーネントの親子構造を実現するために、Componnet
という型を用意しています。コンポーネントの返り値には Component
を使ってください。
controller
import basolato/controller
proc withStylePage*(request:Request, params:Params):Future[Response] {.async.} =
return render(withStyleView())
view
import basolato/view
import ../layouts/application_view
proc impl():Component =
let style = styleTmpl(Css, """
<style>
.background {
height: 200px;
width: 200px;
background-color: blue;
}
.background:hover {
background-color: green;
}
</style>
""")
tmpl"""
$(style)
<div class="$(style.element("background"))"></div>
"""
proc withStyleView*():Component =
let title = "Title"
return applicationView(title, impl())
これをhtmlにコンパイルすると以下のようになります。
<!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
テンプレートは、CSS-in-JS
にインスパイアされた、コンポーネントごとのスタイルを作成するのに便利なタイプです。
スタイル化されたクラス名は、コンポーネントごとにランダムなサフィックスを持つので、複数のコンポーネントが同じクラス名を持つことができます。
また、libsassをインストールすれば、SCSSを使うことができます。
apt install libsass-dev
# or
apk add --no-cache libsass-dev
そして、次のようにスタイルブロックを書きます。
let style = styleTmpl(Scss, """
<style>
.background {
height: 200px;
width: 200px;
background-color: blue;
&:hover {
background-color: green;
}
}
</style>
"""
styleTmpl
関数は Style
型のインスタンスを作ります。
type StyleType* = enum
Css, Scss
proc styleTmpl*(typ:StyleType, body:string):Style
proc element*(self:Style, name:string):string
form
からPOSTリクエストを送信するには、csrf token
を設定する必要があります。basolato/view` のヘルパー関数を利用することができます。
import basolato/view
proc index*():Component =
tmpl"""
<form>
$(csrfToken())
<input type="text", name="name">
</form>
"""
ユーザーの入力値が不正で、入力ページに戻して以前に入力された値を表示したい場合には、old
ヘルパー関数を使います。
API
proc old*(params:JsonNode, key:string, default=""):string
proc old*(params:TableRef, key:string, default=""):string
proc old*(params:Params, key:string, default=""):string
controller
# getアクセス
proc signinPage*(request:Request, params:Params):Future[Response] {.async.} =
return render(signinView())
# postアクセス
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()):Component =
tmpl"""
<input type="text" name="email" value="$(old(params, "email"))">
<input type="text" name="password">
"""
proc signinView*(params=newJObject()):Component =
let title = "SignIn"
return applicationView(title, impl(params))
params
にキー email
があれば値を表示し、なければ空の文字列を表示します。
少し手を加えるだけで、テンプレートエンジンとしてSCFを使うこともできます。
#? stdtmpl | standard
を#? stdtmpl(toString="toString") | standard
に差し替えるbasolato/view
をimportする- 返り値の型を
Component
にする result = ""
をresult = Component.new()
に差し替える- もし非同期で使いたい場合には、返り値の型を
Future[Componente]
にし、{.async.}
プラグマを付けます
結果としてこのようにします。
#? stdtmpl(toString="toString") | standard
#import std/asyncdispatch
#import basolato/view
#proc indexView*(str:string, arr:openArray[string]): Future[Component] {.async.} =
# result = Component.new()
<!DOCTYPE html>
<html lang="en">
<body>
<p>${str}</p>
<ul>
#for row in arr:
<li>${row}</li>
#end for
</ul>
</body>
</html>
コントローラー
proc index(context:Context, params:Params):Future[Response] {.async.} =
let str = "<script>alert('aaa')</script>"
let arr = ["aaa", "bbb", "ccc"]
return render(indexView(str, arr).await)
ビューを作るコマンドに --scf
を付けると、SCFでのビューファイルが作られます。
ducere make layout buttons/success_button --scf
ducere make page login --scf