Templates don’t need to break the browser by Nikolas Martens

Preview:

DESCRIPTION

Despite the fact that most templates in web application are HTML documents, modern template engines are strictly text-manipulating, relying on specific mark-up. Although this makes them universally usable, it also imposes a burden by requiring extra tools to render a template, while making it unreadable for a common tool specialized in mark-up parsing: the browser. In my talk, I’m proposing an approach to writing templates for web applications that leverages the capabilities of HTML to create highly maintainable templates by requiring no tools besides the browser for development and testing.

Citation preview

Nikolas
Notiz
Templating seems like a soved problem. But the solutions I see seem more complicated than necessary. I will show a more light-weight, leaner approach-
Nikolas
Notiz
I enjoy tinkering with the basics of web applications...
Nikolas
Notiz
So I build a new experimental framework for web apps ever now and then. It's a great way to learn.
Nikolas
Notiz
My current project is called watoki http://github.com/watoki
Nikolas
Notiz
I found this article by Ian Dooley. It sounds like a rant but it's actually a really neat aproach. This talk is about his approach and how I implemented it.

<h1>There are no messages</h1>

<?php if($db->fetchOne('select count(*) from messages')) { ?>

<?php $messages = $db->fetchAll('select * from messages'); ?>

<h1>There are <?php echo count($messages); ?> messages:<h1>

<ul>

<li>From <?php echo $message['from']; ?></li>

<?php foreach ($messages as $message) { ?>

<?php } ?>

</ul>

<?php } else { ?>

<php } ?>

Nikolas
Notiz
When Ian says spaghetti, he means files that contain more than one language. Like PHP and HTML in this case.

<h1>There are no messages</h1>

<?php if($db->fetchOne('select count(*) from messages')) { ?>

<?php $messages = $db->fetchAll('select * from messages'); ?>

<h1>There are <?php echo count($messages); ?> messages:<h1>

<ul>

<li>From <?php echo $message['from']; ?></li>

<?php foreach ($messages as $message) { ?>

<?php } ?>

</ul>

<?php } else { ?>

<php } ?>

<h1>There are no messages</h1>

<?php if($db->fetchOne('select count(*) from messages')) { ?>

<?php $messages = $db->fetchAll('select * from messages'); ?>

<h1>There are <?php echo count($messages); ?> messages:<h1>

<ul>

<li>From <?php echo $message['from']; ?></li>

<?php foreach ($messages as $message) { ?>

<?php } ?>

</ul>

<?php } else { ?>

<php } ?>

<h1>There are no messages</h1>

<?php if($db->fetchOne('select count(*) from messages')) { ?>

<?php $messages = $db->fetchAll('select * from messages'); ?>

<h1>There are <?php echo count($messages); ?> messages:<h1>

<ul>

<li>From <?php echo $message['from']; ?></li>

<?php foreach ($messages as $message) { ?>

<?php } ?>

</ul>

<?php } else { ?>

<php } ?>

<h1>There are no messages</h1>

<? if (count($messages)) { ?>

<h1>There are <?= count($messages) ?> messages:<h1>

<ul>

<li>From <?= $message['from']; ?></li>

<? foreach ($messages as $message) { ?>

<?php } ?>

</ul>

<? } else { >

<php } ?>

<? $messages = $messageStore->getAll(); ?>

Nikolas
Notiz
I actually use this style for small projects. Most times, no library is the best library.

<h1>There are no messages</h1>

<? if (count($messages)) { ?>

<h1>There are <?= count($messages) ?> messages:<h1>

<ul>

<li>From <?= $message['from']; ?></li>

<? foreach ($messages as $message) { ?>

<?php } ?>

</ul>

<? } else { >

<php } ?>

<? $messages = $messageStore->getAll(); ?>

<h1>There are no messages</h1>

<? if (count($messages)) { ?>

<h1>There are <?= count($messages) ?> messages:<h1>

<ul>

<li>From <?= $message['from']; ?></li>

<? foreach ($messages as $message) { ?>

<?php } ?>

</ul>

<? } else { >

<php } ?>

<? $messages = $messageStore->getAll(); ?>

<? $messages = $store->getMessages();

'messageCount' => count($messages),

'messages' => $messages

$model = array(

); ?>

<h1>There are no messages</h1>

{{^messageCount}}

<h1>There are {{messageCount}} messages:<h1>

<ul>

<li>From {{from}}</li>

{{#messages}}

{{/messages}}

</ul>

{{/messageCount}}{{#messageCount}}

{{/messageCount}}

<? $messages = $store->getMessages();

'messageCount' => count($messages),

'messages' => $messages

$model = array(

); ?>

<h1>There are no messages</h1>

{{^messageCount}}

<h1>There are {{messageCount}} messages:<h1>

<ul>

<li>From {{from}}</li>

{{#messages}}

{{/messages}}

</ul>

{{/messageCount}}{{#messageCount}}

{{/messageCount}}

Nikolas
Notiz
This is how most modern templating systems work: mostly text manipulation baaed on a special syntax. But still spaghetti according so Ian's definition.
Nikolas
Notiz
Maintainance cost is said to be 80% of a projects lifetime cost. In my experience that's true.

<? $messages = $messageStore->getAll(); ?>

<h1>There are no messages</h1>

<? if (count($messages)) { ?>

<h1>There are <?= count($messages) ?> messages:<h1>

<ul>

<li>From <?= $message['from']; ?></li>

<? foreach ($messages as $message) { ?>

<?php } ?>

</ul>

<? } else { >

<php } ?>

<h1>There are no messages</h1>

{{^messageCount}}

{{/messageCount}}

<h1>There are {{messageCount}} messages:<h1>

<ul>

<li>From {{from}}</li>

{{#messages}}

{{/messages}}

</ul>

{{#messageCount}}

{{/messageCount}}

<? $messages = $messageStore->getAll(); ?>

<h1>There are no messages</h1>

<? if (count($messages)) { ?>

<h1>There are <?= count($messages) ?> messages:<h1>

<ul>

<li>From <?= $message['from']; ?></li>

<? foreach ($messages as $message) { ?>

<?php } ?>

</ul>

<? } else { >

<php } ?>

<h1>There are no messages</h1>

{{^messageCount}}

{{/messageCount}}

<h1>There are {{messageCount}} messages:<h1>

<ul>

<li>From {{from}}</li>

{{#messages}}

{{/messages}}

</ul>

{{#messageCount}}

{{/messageCount}}

Nikolas
Notiz
For me, runability is more important than readability.
Nikolas
Notiz
Runability is also a core believe of TDD
Nikolas
Notiz
This is a recent project of mine.
Nikolas
Notiz
That's what happens when you open the template file in a browser.
Nikolas
Notiz
Rendering the template eveey time you do a small design change seems to take forever even if it's just a second. It kills the flow.
Nikolas
Notiz
Installing and maintaing a development emvironment is a pain if you just want to edit the template. Especially for non-technical people.
Nikolas
Notiz
If we just concentrate on what we want...
Nikolas
Notiz
..you would get someting like that.
Nikolas
Notiz
Ian calls it template animation. So let's go with that.

{ date: '2010-05-10',

slot: '9h - 14h' }

bookings: [

],

{ name: 'April'

… }

months: [

] ...

{ date: '2010-05-10',

slot: '9h - 14h' }

bookings: [

],

{ name: 'April'

… }

months: [

] ...

Nikolas
Notiz
This is the quint-essence of this talk: DOM manipulation instead of text-manipulation
Nikolas
Notiz
How to target the DOM nodes?
Nikolas
Notiz
I use annotations but other methods would be possible.

<div>

Name: <span>John Wayne</span>

Homepage: <a href="http://johnwayne.com">

<span>johnwayne.com</span>

</a>

</div>

<div>

Name: <span>John Wayne</span>

Homepage: <a href="http://johnwayne.com">

<span>johnwayne.com</span>

</a>

</div>

{

person: {

name: "John Wayne",

url : {

href: "http://johnwayne.com",

caption: "johnwayne.com"

}

}

}

<div property="person">

Name: <span property="name">John Wayne</span>

Homepage: <a property="url" href="http://johnwayne.com">

<span property="caption">johnwayne.com</span>

</a>

</div>

{

person: {

name: "John Wayne",

url : {

href: "http://johnwayne.com",

caption: "johnwayne.com"

}

}

}

{

person: {

name: "John Wayne",

url : {

href: "http://johnwayne.com",

caption: "johnwayne.com"

}

}

}

<div property="person">

Name: <span property="name">John Wayne</span>

Homepage: <a property="url" href="http://johnwayne.com">

<span property="caption">johnwayne.com</span>

</a>

</div>

{

person: {

name: "John Wayne",

url : {

href: "http://johnwayne.com",

caption: "johnwayne.com"

}

}

}

<div property="person">

Name: <span property="name">John Wayne</span>

Homepage: <a property="url" href="http://johnwayne.com">

<span property="caption">johnwayne.com</span>

</a>

</div>

{

person: {

name: "John Wayne",

url : {

href: "http://johnwayne.com",

caption: "johnwayne.com"

}

}

}

<div property="person">

Name: <span property="name">John Wayne</span>

Homepage: <a property="url" href="http://johnwayne.com">

<span property="caption">johnwayne.com</span>

</a>

</div>

{

person: {

name: "John Wayne",

url : {

href: "http://johnwayne.com",

caption: "johnwayne.com"

}

}

}

<div property="person">

Name: <span property="name">John Wayne</span>

Homepage: <a property="url" href="http://johnwayne.com">

<span property="caption">johnwayne.com</span>

</a>

</div>

Nikolas
Notiz
Annotations allow the whole animation to become a simple mapping task.

{

person: {

name: "John Wayne",

url : {

href: "http://johnwayne.com",

caption: "johnwayne.com"

}

}

}

<div property="person">

Name: <span property="name">John Wayne</span>

Homepage: <a property="url" href="http://johnwayne.com">

<span property="caption">johnwayne.com</span>

</a>

</div>

Nikolas
Notiz
If you want to this in action, clone https://github.com/rtens/lacarte, open src/rtens/lacarte/web/order/list.html in your browser and navigate through the app. Mind that these are all the actual templates.

<div property="person">

Name: <span property="name">John Wayne</span>

Homepage: <a property="url" href="http://johnwayne.com">

<span property="caption">johnwayne.com</span>

</a>

</div>

<div property="person" typeof="Person">

Name: <span property="name">John Wayne</span>

Homepage: <a property="url" href="http://johnwayne.com">

<span property="caption">johnwayne.com</span>

</a>

</div>

<div vocab="http://schema.org/">

</div>

Nikolas
Notiz
The collision with RDFa is imtentional but avoidable by choosing a different attribute name.
Nikolas
Notiz
You find these slides and my contact info at http://rtens.org