src/happyx/spa/renderer

Renderer ✨

Provides a single-page application (SPA) renderer with reactivity features. It likely contains functions or classes that allow developers to dynamically update the content of a web page without reloading the entire page.

Moving Between Routes 🎈

To move to other location just use route("/path")

Usage 🔨

import happyx

appRoutes("app"):
  "/":
    tDiv:
      "Hello, world!"

Types

App = ref object
  appId*: cstring
  router*: proc (force: bool = false)
AppEventHandler = proc (ev: JsonNode = newJObject()): void
BaseComponentObj {.inheritable.} = object
  uniqCompId*: string
  isCreated*: bool
  slot*: proc (scopeSelf: BaseComponent; inComponent: bool; compName: string;
               inCycle: bool; cycleCounter: var int; compCounter: string): TagRef
  slotData*: TagRef
  created*: ComponentEventHandler ## Calls before first rendering
  exited*: ComponentEventHandler ## Calls after last rendering
  rendered*: ComponentEventHandler ## Calls after every rendering
  pageHide*: ComponentEventHandler ## Calls after every rendering
  pageShow*: ComponentEventHandler ## Calls after every rendering
  beforeUpdated*: ComponentEventHandler ## Calls before every rendering
  updated*: ComponentEventHandler ## Calls after every DOM rendering
  
ComponentEventHandler = proc (self: BaseComponent; ev: JsonNode = newJObject()): void

Vars

application: App = nil
global application variable
componentEventHandlers = newTable(32)
components = newTable(32)
componentsResult = newTable(32)
currentComponent = ""
Current component unique ID
currentRoute: cstring = "/"
Current route path
eventHandlers = newTable(32)
rendererHandlers = newSeq(Natural(0))
requestResult = newTable(32)

Procs

proc html(comp: BaseComponent; data: string) {.inline, ...raises: [], tags: [],
    forbids: [].}
proc html(host, data: string) {.inline, ...raises: [], tags: [], forbids: [].}
proc injectJs(host, script: string) {.inline, ...raises: [ValueError], tags: [],
                                      forbids: [].}
proc js(comp: BaseComponent; script: string) {.inline, ...raises: [ValueError],
    tags: [], forbids: [].}
proc registerApp(appId: cstring = "app"): App {.discardable, inline, ...raises: [],
    tags: [], forbids: [].}

Creates a new Single Page Application

âš  This is Low-level API âš 

use appRoutes proc instead of this because this procedure calls automatically.

proc registerComponent(name: string; component: BaseComponent): BaseComponent {.
    inline, ...raises: [KeyError], tags: [], forbids: [].}

Register a new component.

âš  This is Low-level API âš 

Don't use it because it used in component macro.

proc renderVdom(app: App; tag: TagRef; force: bool = false) {.inline,
    ...raises: [], tags: [], forbids: [].}
Rerender DOM with VDOM
proc route(comp: BaseComponent; path: string) {.inline, ...raises: [], tags: [],
    forbids: [].}
proc route(host, path: string) {.inline, ...raises: [], tags: [], forbids: [].}

Methods

method render(self: BaseComponent): TagRef {.base, inline, ...raises: [], tags: [],
    forbids: [].}
Basic method that needs to overload in components
method reRender(self: BaseComponent) {.base, inline, ...raises: [], tags: [],
                                       forbids: [].}
Basic method that needs to overload in components

Macros

macro appRoutes(name: string; body: untyped): untyped

Registers a new Single page application, creates routing for it and starts SPA.

High-level API

Use it to write your application.

Automatically creates app variable

Basic Usage:

appRoutes("app"):
  "/":
    "Hello, world!"
macro buildComponentHtml(componentName, html: untyped): untyped

buildHtml macro provides building HTML tags with YAML-like syntax. This macro doesn't generate Root tag

Args:

  • html: YAML-like structure.
macro buildHtml(html: untyped): untyped

buildHtml macro provides building HTML tags with YAML-like syntax. This macro doesn't generate Root tag

Args:

  • html: YAML-like structure.
macro buildHtml(root, html: untyped): untyped

buildHtml macro provides building HTML tags with YAML-like syntax.

Args:

  • root: root element. It's can be tag, tag or tTag
  • html: YAML-like structure.

Syntax support:

  • attributes via exprEqExpr
    echo buildHtml(`div`):
      h1(class="myClass", align="center")
      input(`type`="password", align="center")
  • nested tags
    echo buildHtml(`div`):
      tag:
        tag1:
          tag2:
        tag1withattrs(attr="value")
  • if-elif-else expressions
    var
      state = true
      state2 = true
    echo buildHtml(`div`):
      if state:
        "True!"
      else:
        "False("
      if state2:
        "State2 is true"
  • case-of statement:
    type X = enum:
      xA,
      xB,
      xC
    var x = xA
    echo buildHtml(`div`):
      case x:
      of xA:
        "xA"
      of xB:
        "xB"
      else:
        "Other
  • for statement
    var state = @["h1", "h2", "input"]
    echo buildHtml(`div`):
      for i in state:
        i
  • while statement
    var state = 0
    echo buildHtml(`div`):
      while state < 10:
        nim:
          inc state
        "{state}th"
  • rawHtml statement
    echo buildHtml(`div`):
      rawHtml:  """
        <div>
          Hello, world!
        </div>
        """
  • script statement
    echo buildHtml(`div`):
      tScript(...): """
        console.log("Hello, world!");
        """
  • component usage
    component MyComponent
    component MyComponent(field1 = value1, field2 = value2)
    component MyComponent:
      slotHtml
macro buildHtmlSlot(html: untyped; inCycle, inComponent: static[bool]): untyped

buildHtml macro provides building HTML tags with YAML-like syntax. This macro doesn't generate Root tag

Args:

  • html: YAML-like structure.
macro elem(name: untyped): untyped
elem macro is just shortcut for
block:
  var res: Element
  {.emit: "`res` = document.getElementById('name')".}
  res

âš  Works only on JS backend âš 

macro routes(app: App; body: untyped): untyped
Provides JS router for Single page application

Usage:

app.routes:
  "/":
    "Hello, world!"
  
  "/user{id:int}":
    "User {id}"
  
  "/pattern{rePattern:/\d+\.\d+\+\d+\S[a-z]/}":
    {rePattern}
  
  "/get{file:path}":
    "path to file is '{file}'"

Templates

template start(app: App)

Starts single page application

âš  This is Low-level API âš 

use appRoutes proc instead of this because this procedure calls automatically.