Skip to content

Latest commit

 

History

History
404 lines (338 loc) · 8.36 KB

view.md

File metadata and controls

404 lines (338 loc) · 8.36 KB

View

back

Table of Contents

Introduction

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))

XSS

To prevent XSS, you have to use get proc for valiable. It applies xmlEncode inside.

API

proc get*(val:JsonNode):string =

proc get*(val:string):string =

Sample

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>

Component style design

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.

SCSS

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;
  }
}

API

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 =

Helper functions

Csrf Token

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>
"""

old helper

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 template libraries

Other libraries which generate HTML can be chosen for Basolato too. However, each library has it's own benefits and drawbacks.

LibraryBenefitsDrawbacks
htmlgen
  • Nim standard library
  • Easy to use for Nim programmer
  • Available to define plural Procedures in one file
  • Cannot use `if` statement and `for` statement
  • Maybe difficult to collaborate with designer or markup enginner
SCF
  • Nim standard library
  • Available to use `if` statement and `for` statement
  • Easy to collaborate with designer or markup enginner
    • Cannot define plural procedures in one file
    Karax
  • Available to define plural Procedures in one file
  • Available to use `if` statement and `for` statement
    • Maybe difficult to collaborate with designer or markup enginner
    nim-templates
    • Available to define plural Procedures in one file
    • Available to use `if` statement and `for` statement
    • Easy to collaborate with designer or markup enginner
    • Maintained by a person

    Views file should be in app/http/views dir.

    Block component example

    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>

    htmlgen

    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

    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))}

    Karax

    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))