Skip to content

Latest commit

 

History

History
359 lines (301 loc) · 8.61 KB

view.md

File metadata and controls

359 lines (301 loc) · 8.61 KB

ビュー

戻る

目次

イントロダクション

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のコードを書く

${}の中に書いたNimのコードを書くことができます

proc view():Component =
  tmpl"""
    ${ let msg = "Hello" }
    <p>$(msg)</p>
  """

if文

proc indexView(arg:string):Component =
  tmpl"""
    $if arg == "a"{
      <p>A</p>
    }
    $elif arg == "b"{
      <p>B</p>
    }
    $else{
      <p>C</p>
    }
  """

for文

proc indexView(args:openarray[string]):Component =
  tmpl"""
    <li>
      $for row in args{
        <ul>$(row)</ul>
      }
    </li>
  """

while文

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を使ってください。

CSS

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にインスパイアされた、コンポーネントごとのスタイルを作成するのに便利なタイプです。 スタイル化されたクラス名は、コンポーネントごとにランダムなサフィックスを持つので、複数のコンポーネントが同じクラス名を持つことができます。

SCSS

また、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>
"""

API

styleTmpl関数は Style 型のインスタンスを作ります。

type StyleType* = enum
  Css, Scss

proc styleTmpl*(typ:StyleType, body:string):Style

proc element*(self:Style, name:string):string

ヘルパー関数

Csrfトークン

formからPOSTリクエストを送信するには、csrf tokenを設定する必要があります。basolato/view` のヘルパー関数を利用することができます。

import basolato/view

proc index*():Component =
  tmpl"""
    <form>
      $(csrfToken())
      <input type="text", name="name">
    </form>
  """

old関数

ユーザーの入力値が不正で、入力ページに戻して以前に入力された値を表示したい場合には、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をテンプレートエンジンとして使う

少し手を加えるだけで、テンプレートエンジンとしてSCFを使うこともできます。

  1. #? stdtmpl | standard#? stdtmpl(toString="toString") | standardに差し替える
  2. basolato/viewをimportする
  3. 返り値の型をComponentにする
  4. result = ""result = Component.new()に差し替える
  5. もし非同期で使いたい場合には、返り値の型を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