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 attribute

6.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>

{( ... )} 作为一种简短的表达形式存在,并在内部转换为 {{ (...) }},但不要将其用作默认示例。在文档和应用代码中,{{ ... }} 通常更清晰。

{{ ... }}{( ... )} 都会原样输出它们的值。对于不受信任或用户提供的文本,请使用 {[ ... ]},它会对其值进行 HTML 转义(与写 {{ esc(...) }} 相同):

view($comment):
<p>{[ $comment ]}</p>
<p>{[ $this->user->name ]}</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: avoid

6.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: Rendering

view(...) builds and renders the response, but it does not stop PHP execution. Return it from a route guard or let the routine end immediately afterwards. Build composite pages in a single view, or let a view include other view methods inline with {{ ... }}.

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>

All view() parameters are optional and named:

Parameter Does
title Page title, combined with the app title via title()
css / js / defer Extra assets next to the namespace bundles
options Body class list
settings Body data-* attributes
ns Bundle namespace (default app; see chapter 2)
path Browser URL; false keeps the current URL
inline Embed local css/js into the HTML instead of linking
bodyAttrs / htmlAttrs Extra attributes on <body> / <html>
lang Page language
trailing named args Any apply command, e.g. scroll: 0, trans: 'fade'

App-level defaults come from %app props with the same names. The <head> is further fed by %app->description, %app->viewport, %app->themeColor, %app->nonce, %app->head, %app->link and %app->version (the asset cache-buster).

6.9: Apply commands

apply() accepts named arguments where each key is a DOM mutation or UI action. The runtime core provides the basics; resources can register extra commands via app.mod.<name> (such as DOM/toasts for toast: or DOM/dialog for alert:).

DOM mutations

Cmd Argument Effect
inner {selector: html} el.innerHTML = html
outer {selector: html} el.outerHTML = html
before / after {selector: html} Insert adjacent
prepend / append {selector: html} Insert inside, first/last
remove selector or array Remove elements
attr {selector: {attr: value}} Set/remove (null = remove)
class {selector: 'a b -c !d'} Add / remove (-) / toggle (!)
value {selector: value} Form value
data {selector: {key: value}} el.dataset[key]

App state

Cmd Effect
title document.title
lang html.lang
options Body classes (replaces)
settings Body data attributes
path history.pushState (URL changes without reload)
trans View-transition class (forward/backward/...)
scroll int (pixels) or #anchor

Assets (once per href/src)

css, js, defer, add a link or script; already-loaded URLs are ignored.

Navigation and callbacks

Cmd Effect
location Path or true (reload current path); an external URL does location.assign()
call Call app[name]() after the apply

Meta

Cmd Effect
log / error console.log / console.error on the client
phlo Server-side debug trace, logged in the browser console (debug mode)

Resource mods, available once the corresponding resource is loaded:

Cmd Resource
toast DOM/toasts
alert / confirm / prompt DOM/dialog
store DOM/store
sync DOM/store (a mirrored value: applied, never sent on again)
setvar DOM/CSS.var
template DOM/template

No build-time check on apply keys. A typo (innerinnr) is silently ignored. Keep this table at hand, or consult /opt/phlo/docs/apply-protocol.md for the complete, up-to-date reference including edge cases and stream semantics.

Example combining multiple commands:

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',
	)
}

Reference. The DOM and view resources are documented per node in the Manual, generated from the resource files so it never drifts from the code.

最近更新于 2026年8月23日

我们使用必要的cookie来使该网站正常工作。在您的许可下,我们还使用分析工具来改善网站。