6: Views
视图直接位于 .phlo 文件中。视图被转译为返回 HTML 的 PHP 方法。您可以使用 view(...) 来渲染视图。
在一切之前有一个规则:空行结束视图。在 view ...: 之后的第一个空行结束该块;其后的 HTML 将变为控制器代码,构建将停止并显示 HTML outside a view。切勿在视图内部插入空行以进行视觉间隔。
6.1: 声明
匿名视图:
view:
<p>Test</p>
命名视图:
view home:
<h1>Welcome</h1>
带参数的视图:
view greeting($name):
<p>Hello $name</p>
调用它:
method show => view($this->greeting('Jordi'))6.2: 多行块
多行视图会一直运行,直到遇到空行。因此,请不要在视图 HTML 中间放置空行。
view:
<section>
<h1>Welcome</h1>
<p>Intro</p>
</section>
view footer:
<footer>Phlo</footer>6.3: HTML 简写
Phlo 支持紧凑的 id/class 简写:
view:
<p#intro.lead/>
这变成了:
<p id="intro" class="lead"></p>
一个尾随斜杠使得标签在源代码中自闭合,而 Phlo 将其转换为正常的开闭标签。
一个硬性规则:一个标签要么使用简写,要么使用显式的 class/id 属性,绝不能两者兼用。将它们结合在一起可能会导致重复属性,浏览器只保留第一个,默默丢弃动态的那个。当类列表的一部分是动态时,将整个内容写为一个属性;对于完全静态的标签,保留简写:
<a.site-logo href="/"> valid: fully static
<a class="card {( $active ? 'is-active' : void )}"> valid: dynamic, one attribute
<a.card class="$extra"> INVALID: duplicate class attribute6.4: 文本和变量
您可以直接在文本中使用普通变量和简单属性:
view($name):
<p>Hello $name</p>
<p>$this->title</p>
对于方法调用、链式访问或表达式,请使用 {{ ... }}:
view:
<p>{{ $this->label('start') }}</p>
<p>{{ $this->record->title }}</p>
<p>{{ $this->count > 1 ? 'Multiple' : 'One' }}</p>
{( ... )} 作为一种简短的表达形式存在,并在内部转换为 {{ (...) }},但不要将其用作默认示例。在文档和应用代码中,{{ ... }} 通常更清晰。
6.5: 可翻译的视图文本
对于静态可翻译文本,请使用语言简写:
view:
<h1>{nl: Welkom}</h1>
<p>{nl: Hallo wereld}</p>
带参数:
view($name):
<p>{nl: Hallo %s ($name)}</p>
使用简写形式;它更短,并且一目了然地显示文本的源语言。
6.6: 属性
属性值如果没有空格或变量,可以不加引号:
view:
<a href=/contact>Contact</a>
使用引号来表示变量或表达式:
view:
<a href="$this->url">Link</a>
<a href="{{ $this->url('contact') }}">Contact</a>
属性值直接插入 $var、$this->prop 和 %instance->prop,包括带有字面后缀的情况。将普通属性访问包裹在 {{ }} 中是多余的;请将 {{ }} 保留用于调用,将 {( )} 用于表达式:
<a href="%base->view/install"> valid: direct interpolation plus suffix
<a href="{{ %base->view }}/install"> works, but redundant and ugly: avoid6.7: 控制流
将控制流标签放在单独的行上:
view:
<ul>
<foreach $this->items AS $item>
<li>$item->title</li>
</foreach>
</ul>
使用 if:
view:
<if $this->active>
<p>Active</p>
<else>
<p>Inactive</p>
</if>6.8: 渲染
view(...) 渲染响应并停止进一步的请求处理。因此,可以在单个视图中构建复合页面,或者让视图通过 {{ ... }} 内联包含其他视图方法。
route both GET home => view($this)
view:
<main>
{{ $this->hero }}
{{ $this->content }}
</main>
view hero:
<header>
<h1>$this->title</h1>
</header>
view content:
<section>
<p>{nl: Welkom op de site}</p>
</section>
所有 view() 参数都是可选的并且是命名的:
| 参数 | 功能 |
|---|---|
title |
页面标题,通过 title() 与应用标题结合 |
css / js / defer |
除命名空间捆绑外的额外资源 |
options |
主体类列表 |
settings |
主体 data-* 属性 |
ns |
捆绑命名空间(默认 app;见第 2 章) |
path |
浏览器 URL;false 保持当前 URL |
inline |
将本地 css/js 嵌入 HTML 中,而不是链接 |
bodyAttrs / htmlAttrs |
<body> / <html> 上的额外属性 |
lang |
页面语言 |
| 尾部命名参数 | 任何 apply 命令,例如 scroll: 0,trans: 'fade' |
应用级默认值来自于具有相同名称的 %app props。<head> 进一步由 %app->description、%app->viewport、%app->themeColor、%app->nonce、%app->head、%app->link 和 %app->version(资源缓存破坏者)提供。
6.9: 应用命令
apply() 接受命名参数,每个键都是一个 DOM 变更或 UI 操作。运行时核心提供了基础功能;资源可以通过 app.mod.<name> 注册额外命令(例如 DOM/toasts 用于 toast: 或 DOM/dialog 用于 alert:)。
DOM 变更
| Cmd | Argument | Effect |
|---|---|---|
inner |
{selector: html} |
el.innerHTML = html |
outer |
{selector: html} |
el.outerHTML = html |
before / after |
{selector: html} |
插入相邻元素 |
prepend / append |
{selector: html} |
插入内部,第一/最后 |
remove |
selector 或数组 | 移除元素 |
attr |
{selector: {attr: value}} |
设置/移除(null = 移除) |
class |
{selector: 'a b -c !d'} |
添加 / 移除 (-) / 切换 (!) |
value |
{selector: value} |
表单值 |
data |
{selector: {key: value}} |
el.dataset[key] |
应用状态
| Cmd | Effect |
|---|---|
title |
document.title |
lang |
html.lang |
options |
Body 类(替换) |
settings |
Body 数据属性 |
path |
history.pushState(URL 更改而不重新加载) |
trans |
视图过渡类(forward/backward/...) |
scroll |
int(像素)或 #anchor |
资源(每个 href/src 一次)
css、js、defer,添加链接或脚本;已加载的 URL 会被忽略。
导航和回调
| Cmd | Effect |
|---|---|
location |
路径或 true(重新加载当前路径);外部 URL 执行 location.assign() |
call |
在 apply 之后调用 app[name]() |
元信息
| Cmd | Effect |
|---|---|
log / error |
在客户端执行 console.log / console.error |
phlo |
服务器端调试跟踪,记录在浏览器控制台(调试模式) |
资源模块,在相应资源加载后可用:
| Cmd | Resource |
|---|---|
toast |
DOM/toasts |
alert / confirm / prompt |
DOM/dialog |
store |
DOM/store |
setvar |
DOM/CSS.var |
template |
DOM/template |
对 apply 键没有构建时检查。 拼写错误(
inner→innr)会被静默忽略。请随时保留此表,或查阅/opt/phlo/docs/apply-protocol.md以获取完整的、最新的参考,包括边缘情况和流语义。
示例结合多个命令:
route async POST item save {
if (!$item = item::save(%payload)) return apply(
error: 'Save failed',
class: ['[name=title]' => '!error'],
)
apply(
outer: ['#item-'.$item->id => $this->itemView($item)],
toast: 'Saved',
scroll: '#item-'.$item->id,
trans: 'fade',
)
}