Enhance Backend Scripts Documentation
- Added important notes regarding ES Module exports and route parameter access in backend scripts. - Introduced a new section on data binding methods, detailing how to call backend script methods from `.json` configurations. - Included examples demonstrating the correct usage of request parameters and common pitfalls to avoid, improving clarity for developers.
This commit is contained in:
parent
f115e6d782
commit
46ae99948e
3 changed files with 330 additions and 0 deletions
|
|
@ -14,6 +14,12 @@ Backend scripts use the naming convention `<page>.backend.ts` or `<page>.backend
|
|||
└── list.backend.ts # Backend script
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
> **⚠️ No ES Module Exports**: Backend scripts do NOT support ES Module `export` syntax. Simply define functions directly - they will be automatically available based on naming conventions.
|
||||
|
||||
> **⚠️ `$param` Not Available**: Unlike HTML templates, you cannot use `$param.id` directly in backend scripts. Route parameters must be accessed via the `request.params` object passed to your functions.
|
||||
|
||||
## BeforeRender
|
||||
|
||||
The `BeforeRender` function is called before the page is rendered:
|
||||
|
|
@ -248,6 +254,78 @@ function ApiUpdateUser(id: string, data: any, request: Request): any {
|
|||
}
|
||||
```
|
||||
|
||||
## Data Binding Methods (Called from `.json`)
|
||||
|
||||
In addition to `Api` prefixed methods (for frontend calls) and `BeforeRender`, you can define methods that are called directly from the page's `.json` configuration using the `@MethodName` syntax.
|
||||
|
||||
### Naming Convention
|
||||
|
||||
| Call Source | Function Name | Example Call |
|
||||
| ---------------------------- | --------------- | ------------------------------- |
|
||||
| Frontend `$Backend().Call()` | `ApiMethodName` | `$Backend().Call("MethodName")` |
|
||||
| `.json` data binding | `MethodName` | `"$data": "@MethodName"` |
|
||||
| Before render | `BeforeRender` | Automatic |
|
||||
|
||||
### How It Works
|
||||
|
||||
When using `@MethodName` in `.json`, SUI calls the backend function with the **Request object appended as the last argument**:
|
||||
|
||||
```typescript
|
||||
// In .json: "$record": "@GetRecord"
|
||||
// SUI internally calls: GetRecord(request)
|
||||
|
||||
function GetRecord(request: Request): any {
|
||||
// Access route parameters via request.params
|
||||
const id = request.params.id;
|
||||
return Process("models.record.Find", id);
|
||||
}
|
||||
```
|
||||
|
||||
### With Additional Arguments
|
||||
|
||||
You can also pass arguments from `.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"$items": {
|
||||
"process": "@GetItems",
|
||||
"args": ["category_a", 10]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// SUI calls: GetItems("category_a", 10, request)
|
||||
// Arguments from .json come first, request is appended last
|
||||
|
||||
function GetItems(category: string, limit: number, request: Request): any[] {
|
||||
return Process("models.item.Get", {
|
||||
wheres: [{ column: "category", value: category }],
|
||||
limit: limit,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Common Pitfall: Accessing Route Parameters
|
||||
|
||||
❌ **Wrong** - `$param` is not available in backend scripts:
|
||||
|
||||
```typescript
|
||||
function GetRecord(): any {
|
||||
const id = $param.id; // ReferenceError: $param is not defined
|
||||
return Process("models.record.Find", id);
|
||||
}
|
||||
```
|
||||
|
||||
✅ **Correct** - Use `request.params`:
|
||||
|
||||
```typescript
|
||||
function GetRecord(request: Request): any {
|
||||
const id = request.params.id; // Works!
|
||||
return Process("models.record.Find", id);
|
||||
}
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
**`/users/profile/profile.backend.ts`**:
|
||||
|
|
|
|||
|
|
@ -150,6 +150,44 @@ Note: `$header` is only available in JSON configuration, not in HTML templates.
|
|||
}
|
||||
```
|
||||
|
||||
### Calling Backend Script Methods
|
||||
|
||||
Use the `@MethodName` syntax to call functions defined in the page's `.backend.ts` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"$record": "@GetRecord",
|
||||
"$items": {
|
||||
"process": "@GetItems",
|
||||
"args": ["active", 20]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Important**: The Request object is automatically appended as the **last argument** to the backend function.
|
||||
|
||||
**`page.backend.ts`**:
|
||||
|
||||
```typescript
|
||||
// Called from .json as: "$record": "@GetRecord"
|
||||
// Receives: (request)
|
||||
function GetRecord(request: Request): any {
|
||||
const id = request.params.id; // Access route params via request
|
||||
return Process("models.record.Find", id);
|
||||
}
|
||||
|
||||
// Called from .json as: { "process": "@GetItems", "args": ["active", 20] }
|
||||
// Receives: ("active", 20, request)
|
||||
function GetItems(status: string, limit: number, request: Request): any[] {
|
||||
return Process("models.item.Get", {
|
||||
wheres: [{ column: "status", value: status }],
|
||||
limit: limit,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
> **⚠️ Common Mistake**: You cannot use `$param.id` directly in backend scripts. The `$param`, `$query`, etc. variables are only available in HTML templates and `.json` configurations. In backend scripts, access these values via the `request` parameter: `request.params.id`, `request.query.search`, etc.
|
||||
|
||||
## Built-in Functions
|
||||
|
||||
### P\_() - Process Call
|
||||
|
|
|
|||
214
sui/docs/routing.md
Normal file
214
sui/docs/routing.md
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
# Routing
|
||||
|
||||
SUI supports file-system based routing with dynamic route parameters and URL rewriting.
|
||||
|
||||
## File-System Routing
|
||||
|
||||
Pages are organized in directories, with each directory containing a page's files:
|
||||
|
||||
```
|
||||
/pages/
|
||||
├── index/
|
||||
│ ├── index.html
|
||||
│ ├── index.css
|
||||
│ └── index.ts
|
||||
├── about/
|
||||
│ ├── about.html
|
||||
│ └── about.css
|
||||
└── users/
|
||||
├── users.html
|
||||
└── [id]/ # Dynamic route
|
||||
├── [id].html
|
||||
├── [id].css
|
||||
└── [id].ts
|
||||
```
|
||||
|
||||
## Dynamic Routes
|
||||
|
||||
Use square brackets `[param]` to create dynamic route segments:
|
||||
|
||||
| Directory Structure | URL Pattern | Example URL |
|
||||
| ------------------- | ---------------- | -------------------- |
|
||||
| `/users/[id]/` | `/users/:id` | `/users/123` |
|
||||
| `/posts/[slug]/` | `/posts/:slug` | `/posts/hello-world` |
|
||||
| `/[category]/[id]/` | `/:category/:id` | `/electronics/456` |
|
||||
|
||||
### Accessing Route Parameters
|
||||
|
||||
**In HTML templates** - Use `$param`:
|
||||
|
||||
```html
|
||||
<h1>User ID: {{ $param.id }}</h1>
|
||||
<p>Category: {{ $param.category }}</p>
|
||||
```
|
||||
|
||||
**In `.json` configuration**:
|
||||
|
||||
```json
|
||||
{
|
||||
"userId": "$param.id",
|
||||
"$user": {
|
||||
"process": "models.user.Find",
|
||||
"args": ["$param.id"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**In backend scripts** - Via Request object:
|
||||
|
||||
```typescript
|
||||
function GetRecord(request: Request): any {
|
||||
const id = request.params.id;
|
||||
return Process("models.record.Find", id);
|
||||
}
|
||||
```
|
||||
|
||||
> **Note**: `$param` is NOT available as a global variable in backend scripts. You must access route parameters through the `request.params` object.
|
||||
|
||||
## URL Rewriting
|
||||
|
||||
SUI pages require URL rewriting to map clean URLs to `.sui` page files. Configure rewrite rules in `app.yao`:
|
||||
|
||||
```json
|
||||
{
|
||||
"public": {
|
||||
"rewrite": [
|
||||
{ "^\\/assets\\/(.*)$": "/assets/$1" },
|
||||
{ "^\\/users\\/([^\\/]+)$": "/users/[id].sui" },
|
||||
{ "^\\/(.*)$": "/$1.sui" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Rewrite Rule Syntax
|
||||
|
||||
Each rule is a JSON object with a regex pattern as the key and the target path as the value:
|
||||
|
||||
```json
|
||||
{ "REGEX_PATTERN": "TARGET_PATH" }
|
||||
```
|
||||
|
||||
- **REGEX_PATTERN**: A regular expression to match the incoming URL
|
||||
- **TARGET_PATH**: The internal path to route to, can use capture groups (`$1`, `$2`, etc.)
|
||||
|
||||
### Rule Processing Order
|
||||
|
||||
Rules are processed **in order from top to bottom**. The first matching rule wins. Always place more specific rules before general ones.
|
||||
|
||||
### Common Patterns
|
||||
|
||||
#### Static Assets (Passthrough)
|
||||
|
||||
```json
|
||||
{ "^\\/assets\\/(.*)$": "/assets/$1" }
|
||||
```
|
||||
|
||||
Passes asset requests directly without modification.
|
||||
|
||||
#### Simple Dynamic Route
|
||||
|
||||
```json
|
||||
{ "^\\/users\\/([^\\/]+)$": "/users/[id].sui" }
|
||||
```
|
||||
|
||||
Maps `/users/123` to `/users/[id].sui`, making `123` available as `$param.id`.
|
||||
|
||||
#### Nested Dynamic Route
|
||||
|
||||
```json
|
||||
{
|
||||
"^\\/users\\/([^\\/]+)\\/posts\\/([^\\/]+)$": "/users/[id]/posts/[postId].sui"
|
||||
}
|
||||
```
|
||||
|
||||
Maps `/users/123/posts/456` to the nested page, with `$param.id = "123"` and `$param.postId = "456"`.
|
||||
|
||||
#### Catch-All for SUI Pages
|
||||
|
||||
```json
|
||||
{ "^\\/(.*)$": "/$1.sui" }
|
||||
```
|
||||
|
||||
Maps any URL to its corresponding `.sui` file. Place this **last** as a fallback.
|
||||
|
||||
#### Specific Page Override
|
||||
|
||||
```json
|
||||
{ "^\\/dashboard\\/login(.*)$": "/dashboard/login.sui" },
|
||||
{ "^\\/dashboard\\/(.*)$": "/dashboard/[id].sui" }
|
||||
```
|
||||
|
||||
The login page is matched first (specific), then other dashboard pages use dynamic routing.
|
||||
|
||||
### Complete Example
|
||||
|
||||
```json
|
||||
{
|
||||
"public": {
|
||||
"rewrite": [
|
||||
// Static assets - passthrough
|
||||
{ "^\\/assets\\/(.*)$": "/assets/$1" },
|
||||
{ "^\\/images\\/(.*)$": "/images/$1" },
|
||||
|
||||
// Specific pages (before dynamic routes)
|
||||
{ "^\\/blog\\/new$": "/blog/new.sui" },
|
||||
{ "^\\/blog\\/([^\\/]+)\\/edit$": "/blog/[id]/edit.sui" },
|
||||
|
||||
// Dynamic routes
|
||||
{ "^\\/blog\\/([^\\/]+)$": "/blog/[id].sui" },
|
||||
{
|
||||
"^\\/users\\/([^\\/]+)\\/posts\\/([^\\/]+)$": "/users/[id]/posts/[postId].sui"
|
||||
},
|
||||
{ "^\\/users\\/([^\\/]+)$": "/users/[id].sui" },
|
||||
|
||||
// Fallback - must be last
|
||||
{ "^\\/(.*)$": "/$1.sui" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Regex Tips
|
||||
|
||||
| Pattern | Matches | Description |
|
||||
| ----------- | ------------------ | ------------------------------------ |
|
||||
| `([^\\/]+)` | Any segment | Matches characters until next `/` |
|
||||
| `(.*)` | Everything | Matches any characters including `/` |
|
||||
| `(\\d+)` | Numbers only | Matches numeric IDs |
|
||||
| `([a-z-]+)` | Lowercase + hyphen | Matches slugs like `hello-world` |
|
||||
|
||||
### Debugging Rewrite Rules
|
||||
|
||||
1. Check the server logs for route matching information
|
||||
2. Ensure regex escaping is correct (double backslashes in JSON: `\\/` for `/`)
|
||||
3. Test specific URLs to verify capture groups work correctly
|
||||
4. Remember that the `.sui` extension is internal - users access pages without it
|
||||
|
||||
## Route Parameters in Different Contexts
|
||||
|
||||
| Context | Access Method | Example |
|
||||
| --------------- | ------------------- | ------------------------------- |
|
||||
| HTML Template | `{{ $param.id }}` | `<h1>{{ $param.id }}</h1>` |
|
||||
| `.json` Config | `"$param.id"` | `"userId": "$param.id"` |
|
||||
| Backend Script | `request.params.id` | `const id = request.params.id;` |
|
||||
| Frontend Script | Read from DOM | `document.body.dataset.id` |
|
||||
|
||||
### Frontend Access Pattern
|
||||
|
||||
Since frontend scripts run in the browser, route params aren't directly available. Pass them via data attributes:
|
||||
|
||||
**HTML**:
|
||||
|
||||
```html
|
||||
<div id="page" data-id="{{ $param.id }}">
|
||||
<!-- content -->
|
||||
</div>
|
||||
```
|
||||
|
||||
**Frontend TypeScript**:
|
||||
|
||||
```typescript
|
||||
const pageEl = document.getElementById("page");
|
||||
const id = pageEl?.dataset.id;
|
||||
```
|
||||
Loading…
Add table
Reference in a new issue