6: Views
Views leven direct in .phlo-bestanden. Een view transpileert naar een PHP-methode die HTML retourneert. Je rendert een view met view(...).
Eén regel voor alles: een lege regel sluit de view. De eerste lege regel na view ...: beëindigt het blok; HTML daarna wordt controllercode en de build stopt met HTML buiten een view. Voeg nooit een lege regel binnen een view toe voor visuele ruimte.
6.1: Declaratie
Anonieme view:
view:
<p>Test</p>
Genomineerde view:
view home:
<h1>Welcome</h1>
Weergave met argumenten:
view greeting($name):
<p>Hello $name</p>
Het noemen van het:
method show => view($this->greeting('Jordi'))6.2: Meerdere regels blokken
Een multiline view draait totdat er een lege regel is. Dus voeg geen lege regels toe in het midden van de view HTML.
view:
<section>
<h1>Welcome</h1>
<p>Intro</p>
</section>
view footer:
<footer>Phlo</footer>6.3: HTML afkortingen
Phlo ondersteunt compacte id/class afkortingen:
view:
<p#intro.lead/>
Dit wordt:
<p id="intro" class="lead"></p>
Een afsluitende schuine streep maakt een tag zelfsluitend in de bron, en Phlo verandert het in een normale open/sluit tag.
Één harde regel: een tag gebruikt OF de shorthand OF een expliciete class/id attribuut voor die eigenschap, nooit beide. Het combineren ervan kan een duplicaatattribuut genereren en de browser behoudt alleen de eerste, terwijl de dynamische stilletjes wordt verworpen. Wanneer een deel van een class-lijst dynamisch is, schrijf het geheel als één attribuut; houd shorthands voor volledig statische tags:
<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: Tekst en variabelen
Je kunt gewone variabelen en eenvoudige eigenschappen direct in tekst gebruiken:
view($name):
<p>Hello $name</p>
<p>$this->title</p>
Voor methode-aanroepen, ketenaccess, of expressies, gebruik {{ ... }}:
view:
<p>{{ $this->label('start') }}</p>
<p>{{ $this->record->title }}</p>
<p>{{ $this->count > 1 ? 'Multiple' : 'One' }}</p>
{( ... )} bestaat als een korte expressievorm en wordt intern vertaald naar {{ (...) }}, maar gebruik het niet als het standaardvoorbeeld. In documentatie en app-code is {{ ... }} meestal duidelijker.
Zowel {{ ... }} als {( ... )} geven hun waarde rauw weer. Voor onbetrouwbare of door gebruikers aangeleverde tekst, gebruik {[ ... ]}, dat zijn waarde HTML-escapet (hetzelfde als het schrijven van {{ esc(...) }}):
view($comment):
<p>{[ $comment ]}</p>
<p>{[ $this->user->name ]}</p>
Reik {[ ... ]} aan wanneer de waarde tekens kan bevatten die een browser als markup zou interpreteren; houd {{ ... }} voor waarden die je al veilig hebt gemaakt.
6.5: Vertaalbare weergavetekst
Voor statische vertaalbare tekst, gebruik de taalkorte aanduiding:
view:
<h1>{nl: Welkom}</h1>
<p>{nl: Hallo wereld}</p>
Met argumenten:
view($name):
<p>{nl: Hallo %s ($name)}</p>
Gebruik de afkorting hiervoor; het is korter en toont in één oogopslag welke brontaal de tekst heeft.
6.6: Attributen
Attributwaarden zonder spaties of variabelen kunnen onomkaderd blijven:
view:
<a href=/contact>Contact</a>
Met variabelen of expressies, gebruik aanhalingstekens:
view:
<a href="$this->url">Link</a>
<a href="{{ $this->url('contact') }}">Contact</a>
Attributwaarden interpoleren $var, $this->prop en %instance->prop direct, inclusief met een letterlijke suffix. Het omhullen van eenvoudige property-toegang in {{ }} is overbodig; reserveer {{ }} voor aanroepen en {( )} voor expressies:
<a href="%base->view/install"> valid: direct interpolation plus suffix
<a href="{{ %base->view }}/install"> works, but redundant and ugly: avoid6.7: Controleflow
Gebruik control-flow-tags op hun eigen regels:
view:
<ul>
<foreach $this->items AS $item>
<li>$item->title</li>
</foreach>
</ul>
Met 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 (
inner→innr) is silently ignored. Keep this table at hand, or consult/opt/phlo/docs/apply-protocol.mdfor 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.
Laatst bijgewerkt op 23-08-2026