add core view extension with JTE and Thymeleaf support

This commit is contained in:
Relism
2026-04-21 00:32:09 +02:00
parent 34fd74068a
commit 9e19f439be
93 changed files with 2200 additions and 1404 deletions
@@ -0,0 +1,23 @@
# Architecture
`flash-ext-view-thymeleaf` has two layers:
1. **Boot-time**
- `ThymeleafExtension` installs runtime + annotation processor.
- `ThymeleafTargetResolver` validates handlers and resolves `@Template` / `@Fragment`.
- Resolved targets are cached per handler class.
2. **Request-time**
- Handler builds local `ViewModel`.
- Runtime injects globals under reserved `global` namespace and merges local model.
- Thymeleaf renders template or fragment.
## Handler Contract
- Must extend `ThymeleafHandler`.
- Must have route annotation (`@Route`, `@GET`, `@POST`, ...).
- Must declare exactly one view annotation:
- `@Template`
- `@Fragment`
Invalid configurations fail fast at startup.
@@ -0,0 +1,16 @@
# Fragments
Use `@Fragment` for Thymeleaf fragment responses.
```java
@GET("/users/table")
@Fragment(template = "fragments/users", value = "rows")
public final class UsersRows extends ThymeleafHandler {
@Override
public ViewModel render(dev.relism.models.Request req) {
return ViewModel.of("users", List.of());
}
}
```
If `value` is blank, runtime uses the configured default fragment (default: `content`).
@@ -0,0 +1,35 @@
# Handlers
Use `ThymeleafHandler` for class-based Thymeleaf routes.
## Lifecycle
- `onViewInit()` runs once at boot.
- `render(...)` runs per request.
Use `onViewInit()` to cache dependencies via `require(...)`.
## Example
```java
@GET("/dashboard")
@Template("pages/dashboard")
public final class DashboardPage extends ThymeleafHandler {
private DashboardService service;
@Override
protected void onViewInit() {
service = require(DashboardService.class);
}
@Override
public ViewModel render(dev.relism.models.Request req) {
return ViewModel.empty()
.with("title", "Dashboard")
.with("stats", service.stats());
}
}
```
Use `render(Request, Response)` when you need response access while building model variables.
@@ -0,0 +1,31 @@
# Model and Globals
`ViewModel` (from `flash-ext-view-core`) is the per-request model builder.
## Merge Order
Runtime merge order is:
1. all extension globals under reserved `global` namespace
2. local handler model
Handlers cannot set top-level `global`; runtime throws fail-fast to prevent namespace collisions.
## Globals
Register globals on extension setup:
```java
new ThymeleafExtension()
.addGlobal("appName", req -> "Flash")
.addGlobal("path", req -> req.path());
```
Template usage:
```html
<span th:text="${global.appName}"></span>
<span th:text="${global.path}"></span>
```
Keep globals cheap: no blocking I/O or heavy allocations.
@@ -0,0 +1,17 @@
# Performance
`flash-ext-view-thymeleaf` is optimized for low overhead on request path.
## Current runtime choices
- Handler view metadata resolved once and cached.
- Global/local model merge done in a single pass.
- Thymeleaf target metadata resolved and cached per handler class.
- No engine-agnostic adapter indirection.
## Best practices
- Cache services in `onViewInit()`.
- Keep `addGlobal(...)` resolvers cheap and side-effect free.
- Build only the model fields needed by template.
- Avoid blocking I/O in `render(...)`; delegate to precomputed service data when possible.