yao/sui/docs/data-binding.md
Max 097fb73416 Refactor SUI Command Arguments and Enhance Default Template Handling
- Updated the `build` and `watch` commands to accept a single argument for `<sui>` and made the `<template>` argument optional, improving usability.
- Introduced a default template assignment for the `agent` SUI, ensuring a more intuitive setup for users.
- Enhanced error messages for command usage to provide clearer guidance on expected input format.
- Added new OpenAPI file to the bindata, improving the framework's capabilities for API integration.
2026-01-01 10:28:45 +08:00

6.1 KiB

Data Binding

SUI provides built-in variables and functions for accessing request data and executing server-side logic.

Built-in Variables

Request Variables

Variable Description Example
$payload POST request body {{ $payload.name }}
$query URL query parameters {{ $query.search }}
$param Route parameters {{ $param.id }}
$cookie Request cookies {{ $cookie.session }}

URL Variables

Variable Description Example Value
$url.path URL path /users/123
$url.host Full host example.com:8080
$url.domain Domain only example.com
$url.scheme Protocol https
$url.url Full URL https://example.com/users

Context Variables

Variable Description Example Value
$theme Current theme light, dark
$locale Current locale en-us, zh-cn
$timezone System timezone Asia/Shanghai
$direction Text direction ltr, rtl
$global Global data from __data.json { title: "App" }
$auth OAuth authorized info (if guard is oauth) { user_id: "123" }

Usage Examples

Query Parameters

URL: /search?q=hello&page=2

<h1>Search: {{ $query.q }}</h1>
<p>Page: {{ $query.page ?? 1 }}</p>

Route Parameters

Route: /users/[id]/posts/[postId] URL: /users/123/posts/456

<h1>User {{ $param.id }}</h1>
<p>Post {{ $param.postId }}</p>

POST Payload

<form method="POST">
  <input name="email" value="{{ $payload.email }}" />
  <div s:if="{{ $payload.error }}">{{ $payload.error }}</div>
</form>

Theme and Locale

<html class="{{ $theme }}" lang="{{ $locale }}">
  <body dir="{{ $direction }}">
    <h1>{{ $global.title }}</h1>
  </body>
</html>

Data Configuration (<page>.json)

Define page data using JSON configuration:

Static Data

{
  "title": "My Page",
  "items": [
    { "id": 1, "name": "Item 1" },
    { "id": 2, "name": "Item 2" }
  ]
}

Process Calls

{
  "$users": "models.user.Get",
  "$settings": {
    "process": "models.settings.Find",
    "args": [1]
  }
}

Keys starting with $ trigger process calls. The result is available as the variable name (without $).

Using Request Variables

{
  "$user": {
    "process": "models.user.Find",
    "args": ["$param.id"]
  },
  "searchQuery": "$query.q",
  "currentPath": "$url.path"
}

Available request variables in JSON config:

  • $query.<name> - Query parameters
  • $param.<name> - Route parameters
  • $payload.<name> - POST payload
  • $header.<name> - Request headers
  • $url.path / $url.host / $url.domain / $url.scheme

Note: $header is only available in JSON configuration, not in HTML templates.

Complex Example

{
  "pageTitle": "User Profile",
  "userId": "$param.id",
  "$user": {
    "process": "models.user.Find",
    "args": ["$param.id"]
  },
  "$posts": {
    "process": "models.post.Get",
    "args": [
      {
        "wheres": [{ "column": "user_id", "value": "$param.id" }],
        "limit": 10
      }
    ]
  },
  "isOwner": "$query.edit == 'true'"
}

Built-in Functions

P_() - Process Call

Call a Yao process directly in templates:

<!-- Simple call -->
<span>{{ P_('utils.formatDate', createdAt) }}</span>

<!-- With multiple arguments -->
<span>{{ P_('utils.calculate', price, quantity, discount) }}</span>

<!-- In conditions -->
<div s:if="{{ P_('auth.hasPermission', 'admin') }}">Admin Panel</div>

True() / False()

Check boolean values:

<div s:if="{{ True(user) }}">User exists</div>
<div s:if="{{ False(error) }}">No error</div>

<!-- Equivalent to -->
<div s:if="{{ user != null && user != false && user != 0 }}">User exists</div>

Empty()

Check if array or object is empty:

<div s:if="{{ Empty(items) }}">No items</div>
<div s:if="{{ !Empty(items) }}">{{ items.length }} items found</div>

<!-- Works with objects too -->
<div s:if="{{ Empty(settings) }}">No settings configured</div>

Global Data (__data.json)

Define global data available to all pages:

/templates/<template>/__data.json:

{
  "title": "My Application",
  "version": "1.0.0",
  "company": {
    "name": "ACME Inc",
    "email": "contact@acme.com"
  },
  "navigation": [
    { "label": "Home", "href": "/" },
    { "label": "About", "href": "/about" }
  ]
}

Access in templates:

<title>{{ $global.title }}</title>
<footer>© {{ $global.company.name }}</footer>

<nav>
  <a s:for="{{ $global.navigation }}" s:for-item="item" href="{{ item.href }}">
    {{ item.label }}
  </a>
</nav>

Backend Script Data

Data returned from BeforeRender is merged with page data:

<page>.backend.ts:

function BeforeRender(request: Request): Record<string, any> {
  return {
    user: Process("session.Get", "user"),
    notifications: Process("models.notification.Get", {
      wheres: [{ column: "read", value: false }],
      limit: 5,
    }),
    serverTime: new Date().toISOString(),
  };
}

<page>.html:

<div s:if="{{ user }}">
  Welcome, {{ user.name }}!
  <span s:if="{{ !Empty(notifications) }}">
    {{ notifications.length }} new notifications
  </span>
</div>
<footer>Server time: {{ serverTime }}</footer>

Data Priority

When the same key exists in multiple sources, priority is:

  1. BeforeRender (highest) - Backend script data
  2. <page>.json - Page data configuration
  3. __data.json (lowest) - Global data
// BeforeRender returns { title: "From Backend" }
// page.json has { title: "From JSON" }
// __data.json has { title: "Global Title" }

// Result: title = "From Backend"