2: Views
Views live directly in your .phlo files and compile to PHP methods that return HTML. In this chapter you replace the hello route with the first real poll page: a question and a row of answer buttons. You learn the view block, the dot shorthand, variables, and the one rule that bites everyone once: a blank line closes the view.
2.1: From route to view
Replace the contents of poll.phlo:
prop question = 'Which stack wins?'
route GET poll => $this->home
method home => view($this, 'Phlo Poll')
view:
<main#app.poll>
<h1>$this->question</h1>
</main>
Top to bottom: prop question is a static property on the poll class. The route maps GET /poll to the home method. home calls view($this, 'Phlo Poll'): the second argument becomes the page title, and passing $this as the body renders the file's anonymous view, the block that starts at view: without a name.
Reload http://localhost/poll. You see the question as a heading on an unstyled page.
2.2: Dot shorthand
Look at the opening tag again:
<main#app.poll>
Phlo expands #id and .class directly on the tag name:
<main id="app" class="poll">
You can stack classes (<div.card.wide>) and a trailing slash self-closes a tag in the source: <div.spacer/> becomes <div class="spacer"></div>. One caution that will matter later: attribute values that contain variables or slashes need quotes, so write <a href="/poll"> and <form action="/poll/vote/$id">.
Change <main#app.poll> to <main#app.poll.wide>, reload, and inspect the element: the class list now reads poll wide. Change it back.
2.3: A blank line closes the view
A view block ends at the first blank line. Everything after that blank line is controller code again. This is by design: it is how you put several views in one file. It also means you must never insert a blank line inside a view for visual spacing; the HTML below it would leak into controller code and the build stops with HTML outside a view.
view:
<h1>One view</h1>
<p>Still the same view, no blank lines.</p>
view footer:
<p>A second, named view. The blank line above ended the first one.</p>
Try it: put a blank line between <h1> and </main> in your view, reload, and read the error page. It names the exact file, line, and the view that was closed. Remove the blank line and the page is back.
2.4: Variables, expressions, and composition
Inside a view you have three levels of expression:
$varand$this->propfor plain values{{ expr }}for method calls and anything with arguments<foreach>and<if>tags, always on their own line
Extend poll.phlo with a static list of options and a second view:
prop question = 'Which stack wins?'
prop options = ['Phlo', 'Next.js', 'Laravel', 'Rails']
route GET poll => $this->home
method home => view($this, 'Phlo Poll')
view:
<main#app.poll>
<h1>$this->question</h1>
{{ $this->choices }}
</main>
view choices:
<section.card>
<foreach $this->options AS $option>
<button>$option</button>
</foreach>
</section>
{{ $this->choices }} renders the named view choices inline; this is how you compose pages, because view(...) itself may only be called once per request. The <foreach> tag loops over the prop; note that it stands on its own line, never inline inside other HTML.
Reload http://localhost/poll: the question plus four plain buttons. Ugly, but alive. Next chapter: CSS.