Hydrogen Templates for Front-End Developers
Written by Kyek
For Hydrogen Version 0.3.0
DISCLAIMER
Hydrogen is currently in Alpha, meaning that it's in active development and the API may change from version to version in ways that could break existing code. There will be no deprecation until the first major release. So if you use Hydrogen, remember to check the changelogs when you update. They will tell you what needs to be changed in your code. This guide will also be updated to stay current with the latest codebase.
Before You Begin!
This guide is intended for front-end developers writing templates/themes for Hydrogen-powered webapps. If that's you, and you're also the person writing the rest of the webapp, it would be a fabulous idea to read the Hydrogen Overview before you read this. If you're interested in writing your own tags and filters for the 'hydrogen' templating engine, read the Back-End Development guide after you've completed this one.
The Engines
Hydrogen comes with two templating engines that you can choose from: purephp, and the aptly-named hydrogen.
If you've used the Hydrogen toolkit before now, you're probably already familiar with the 'purephp' engine. It's the same basic PHP views that are covered in the Hydrogen Overview, so I won't spend time covering that here. The only difference in Hydrogen 0.3+ is that you must specify that engine in the autoconfig file, and pay attention to view caching
The far far superior view engine, though, is the new default simply called 'hydrogen'. It minimizes duplicate code, makes your code MUCH cleaner, and makes it much easier to maintain. The engine uses the Django syntax, which you may be used to from other popular engines like Twig, Djerb, H2O, NDjango, Smarty, and more. The hydrogen engine is what I'll be covering in this guide.
A Note About ALL Hydrogen Templating Engines
All Hydrogen templating engines, present and future, compile into raw PHP code. This means that it can save basic .php files built from your templates, which prevents it from having to re-compile the template with each request and allows it to execute just as fast as if you wrote the PHP yourself. The other big benefit is that your PHP opcode cache (assuming you're using one) will cache the file so that it comes straight from RAM. Crazy crazy fast, and, like the rest of Hydrogen, avoids all stat() calls.
Your First Template: Variables and Filters
Let's start off easy: a basic HTML page to welcome a user to your site. In this example, I'll assume the controller called the template with the following code:
View::setVar('user', array(
"name" => "magik",
"age" => 26
));
View::setVar('siteName', 'Example.com');
View::load('welcome');So you have two variables you can use: user (with name and age underneath it) and siteName. You could write welcome.tpl.php (or whatever extension you've chosen in the autoconfig) like this:
<!doctype html>
<html>
<head>
<title>{{siteName}}</title>
</head>
<body>
<div id="content">
<p>Welcome to {{siteName}}, {{user.name|upper}}!</p>
<p>You're {{user.age}} years old.</p>
</div>
</body>
</html>When you load that page, what you'll see is:
Quote
You're 26 years old.
Now we know three important things:
- Putting a variable name inside {{ }} will print that variable on the page.
- You can access 'properties' of a variable with a dot (user.name).
- You can apply filters to a variable by following it with a pipe (|) symbol and a filter name. The filter 'upper' will print the variable in all uppercase.
Filters are powerful, and can change a variable in virtually any way-- from something simple like reversing the letters, to something much more complex like parsing markdown format or doing code syntax highlighting and outputting HTML.
Filters can also take arguments. For example, we can add this to the content div as well:
<p>In two years, you'll be {{user.age|add:2}}!In this case, we're calling the 'add' filter, and passing the number '2' to it. This will output the number 28.
Filters can be chained. For example, we can chain the 'lower' filter (forcing the variable to display in all lowercase) with the 'capfirst' filter (capitalizing the first character of the variable) in order to make a variable print in all lowercase with a capital first letter. For example, let's say the variable text contains the word "hElLo". We can make it display "Hello" with this line:
{{text|lower|capfirst}}When chaining filters, order matters! The above works because we made the first character a capital AFTER shifting the whole thing to lowercase. Unlike this line:
{{text|capfirst|lower}}...which outputs "hello" because we made the first character a capital and then changed all characters to lowercase.
Other filters will be covered later in this guide.
Using Block Tags
Variables and filters are good, but sometimes you need more advanced logic and functionality in your templates. Block tags to the rescue! Imagine a controller calls a view like this:
View::setVar('user', array(
'logged_in' => true,
'name' => 'magik',
'posts' => 500
));
View::load('profile');And now we want a template to display one thing if the user is logged in, and something else if he isn't. Using block tags, now we can!
{% if user.logged_in %}
Welcome, {{user.name}}!
{% else %}
Welcome, guest!
{% endif %}
Block tags are contained by {% %} and don't need a variable to work their magic. The 'if' tag used above is just one of many handy block tags, and the condition statement can be extremely complex. Here's something a little more involved:
{% if user.posts = 0 %}
You should start posting!
{% else if user.posts > 0 and user.posts < 10 %}
You don't have a lot of posts -- try getting more involved!
{% elseif user.posts >= 10 && user.posts < 50 %}
Looks like you've gotten a good start! Have you started a project yet?
{% else %}
You da man!
{% endif %}I intentionally used a few synonyms above -- 'else if' is the same thing as 'elseif', 'and' is the same thing as '&&', '=' is the same thing as '=='. The Hydrogen expression parser is as forgiving as possible ;-).
Some other notes about expressions:
- Legal operators: - + / * %
- Legal comparisons: < > = == != <= >=
- Logic: and && or || not !
- Grouping: ( )
- Concatenation: .
- Advanced operators: in empty exists
Most of the above is self-explanatory, but I'd like to cover the super-useful advanced operators before we move on:
"in"
Tests to see if a value is "in" an array or if a string is "in" another string. Example:
{% if "name" in user %}
You must be logged in, because you have a name!
{% elseif "m" in user.name %}
You have an 'm' in your name!
{% endif %}"empty"
Checks to see if an array or string is empty. Example:
{% if not empty user.name %}
I know you have a name that's not ""!
{% endif %}
{% if empty posts %}
There are no posts :(
{% endif %}"exists"
Checks to see if a certain variable has even been set. Example:
{% if not exists user.age %}
I don't have an age for you.
{% endif %}You can also use these expressions with other tags. For example, the eval tag:
If your brother is three years older than half your age, he is: {% eval user.age / 2 + 3 %}Other block tags will be covered later in this guide.
Template Inheritance
This is what really makes Hydrogen templates worth using. All of the above tricks are cool, but they're all things that are technically possible using the 'purephp' engine. Template inheritance is not, and it's what will make your site incredibly easy to maintain. Imagine the following simple template:
base.tpl.php
<!doctype html>
<html>
<head>
<title>{% block title %}Example.com{% endblock %}</title>
{% block header.css %}
<link href="/css/main.css" rel="stylesheet" type="text/css" />
{% endblock %}
{% block header.js %}
{% endblock %}
</head>
<body>
<div id="content">
{% block content %}
{% endblock %}
</div>
</body>
</html>
This template has no real content, but it can contain a whole page layout. What you'll notice, though, are all the {% block ____ %} tags up there. Those define blocks of space that you can "override" with other templates. Here's an example of a template that will display using the above layout:
welcome.tpl.php
{% extends base %}
{% block content %}
Welcome to the site, {{user.name}}!
{% endblock %}This is the beauty of template inheritance. It follows the DRY ("Don't Repeat Yourself") model perfectly: We don't need to rewrite any of the <html> or <head> or any of that, and we don't need to worry with "including" a global header or global footer file in this template. We just tell it to extend 'base' (Note the file name -- "extends base" tells it to extend the file "base.tpl.php"), and now we can override any of the blocks that we've defined in the parent template.
You can also override more than one block. For example, what if you're loading a page that requires its own separate CSS file? You could do this:
other.tpl.php
{% extends base %}
{% block title %}Other Page{% endblock %}
{% block header.css %}
{% parentblock %}
<link href="/css/other.css" rel="stylesheet" type="text/css" />
{% endblock %}
{% block content %}
Now I can use <span class="other-style">other styles!</span>
{% endblock %}There are two important details to learn from that example:
- Overriding a block will completely replace any contents it already had. If you look at the base.tpl.php template, you'll see that "title" had Example.com in it. This template, when loaded, will replace the title with "Other Page".
- You can still print the old contents of the block you're overriding by using the {% parentblock %} tag. Notice that the "header.css" block in the base template already had a CSS file in it? Using {% parentblock %} in our extended version of that block will include that line. As a result, the "header.css" block will contain the <link> tag for main.css AND other.css.
You can also extend extended templates!
Load this template, and it will have both main.css and other.css, but different content:
another.tpl.php
{% extends other %}
{% block content %}
Other content!
{% endblock %}Built-in Block Tags
AUTOESCAPE <on/off>
By default, variables printed by the variable tag {{varName}} will be automatically escaped. For example, if varName contains the following string:
<b>Bold text!</b>
Then putting {{varName}} in your template will output this:
<b>Bold text!<b>
This is an important safety feature, but sometimes you may wish to turn it off. You can use
{% autoescape off %} ... {% endautoescape %} to surround any block of code for which autoescaping should be turned off. You can nest autoescaping too, so if you need to turn it back on inside of a section where it's off, that is also possible:
{% autoescape off %}
This is some {{varName}} {# Output: This is some <b>Bold text!</b> #}
{% autoescape on %}
This is NOT {{varName}} {# Output: This is NOT <b>Bold text!<b> #}
{% endautoescape %}
But this is still {{varName}} {# Output: But this is still <b>Bold text!</b> #}
{% endautoescape %}BLOCK <name>
Allows you to define or override an inherit-able template section. See the "Template Inheritance" section above for usage.
COMMENT
Comment tags are a default part of the Hydrogen template language:
{# This is a comment and {% these %} {{tags}} won't execute! #}For commenting out large sections of code that may include other comment tags, though, the comment block becomes useful. For example, none of the following text will display:
{% comment %}
This is some text with a {# comment #} in it!
{% endcomment %}
EVAL <expression>
Evaluates an expression and prints the result. Examples:
{% eval (2 + 2) / 4 %} {# Output: 1 #}
{% eval "W" in "Hello World!" %} {# Output: true #}
For more examples of expressions, see the "Using Block Tags" section above. Note that any boolean values printed by 'eval' will print as either 'true' or 'false'.
EXTENDS
Used in template inheritance to extend some other template and modify its BLOCK tags. See the "Template Inheritance" section above for usage.
FILTER <filter[|filter|filter|...]>
Allows you to apply a variable filter to any block of text, using the same filter syntax you would use in a variable tag. For example:
{% filter upper %}This text is big!{% endfilter %}
{# Output: THIS TEXT IS BIG! #}
{% filter lower|capfirst %}hELLo wORLd!{% endfilter %}
{# Output: Hello world! #}FOR [key,] <value> in <array> ... EMPTY
Iterates through every item of an array, with an optional "EMPTY" section that defines what to display if the array is empty. This tag will automatically create a forloop variable that will exist only inside the loop. forloop has the following properties:
forloop.counter The current iteration of the loop (1-indexed)
forloop.counter0 The current iteration of the loop (0-indexed)
forloop.revcounter The number of iterations from the end of the loop (1-indexed)
forloop.revcounter0 The number of iterations from the end of the loop (0-indexed)
forloop.first True if this is the first time through the loop
forloop.last True if this is the last time through the loop
forloop.parentloop For nested loops, this is the loop "above" the current one
Some examples:
{% for post in posts %}
<p>Author: {{post.author}}</p>
<p>Posted on: {{post.date}}</p>
<p>Article: {{post.content}}</p>
{% empty %}
<p>No posts!</p>
{% endfor %}
{% for name, age in people %}
<p>Person {{forloop.counter}}, named "{{name}}", is {{age}} years old.</p>
{% endfor %}
{
"blog_name": "My Blog",
"posts": [
{% for post in posts %}
{
"author": "{{post.author}}",
"date": "{{post.date}}",
"article": "{{post.article}}"
}{% if not forloop.last %},{% endif %}
{% endfor %}
]
}IF <expression> ... ELSEIF ... ELSE
Allows code to be displayed based on the outcome of an expression. See the "Using Block Tags" section above for usage and expression examples.
INCLUDE <view>
Includes another view within this template, at the location of the include tag. The given view name can be hardcoded in quotes, or a variable name can be used to dynamically include a view. Examples:
{% include "fragments/sidebar" %}
{% for widget in widgets %}
{% include widget %}
{% endfor %}
PARENTBLOCK
Allows the contents of a parent block to be echoed in a block that's being overridden. See the "Template Inheritance" section above for usage.
SET <variable> [value]
Sets a variable to a certain value, either by specifying that value in the tag or by wrapping the tag around other template bits. Examples:
{% set myVar 4 %} {# myVar is now 4 #}
{% set myVar "I like turtles!" %} {# myVar is set to the specified string #}
{% set greeting "My name is \"Dan\"!" %} {# Inner quotes must be escaped with a backslash #}
{% set myVar pageTitle|upper %} {# myVar is now set to the uppercase version of the pageTitle variable #}
{% set myVar true %} {# myVar is now true #}
{% set bigtext %}
This is some text on the {{pageTitle}} page!
{% endset %}
{# The bigtext variable now contains the rendered version of everything between the set and endset tags. #}Note that an {% endset %} tag is required when no value is specified in the set tag itself, and is prohibited when there is.
TEMPLATETAG <tagtype>
If you want to have Hydrogen-style template tags like {% or {{ actually printed on the page, putting that plaintext in the page itself will cause Hydrogen to attempt to parse it, likely resulting in an error. The 'templatetag' tag is the solution to this problem. The tagtype argument can be any of these things:
- openblock (prints {%)
- closeblock (prints %})
- openvariable (prints {{)
- closevariable (prints }})
- opencomment (prints {#)
- closecomment (prints #})
Example:
{% templatetag openblock %} autoescape off {% templatetag closeblock %}
{# prints: {% autoescape off %} #}URL [base_url] [argument] [key=value] [...]
Dynamically builds a URL, either relative to the base app URL in the config file, or absolute (if the included base_url argument starts with 'http'). Arguments can be either hardcoded if surrounded by quotes ("blog") or dynamic by using a variable name. Arguments with a '=' sign in them will be used as query parameters (?post=somepost&author=someguy), while arguments without a '=' sign will be treated as additional 'folders'. The following examples assume that "http://example.com" is the base URL given in the config file:
{% url mycontroller/myfunction postNum "magik" %}
{# http://example.com/mycontroller/myfunction/4/magik #}
{% url %}
{# http://example.com #}
{% url / controllerVar "home" name=userName %}
{# http://example.com/dynamic_controller/home/?name=magik #}
{% url index.php/blog/search title=searchTitle author="kyek" %}
{# http://example.com/index.php/blog/search/?title=coding&author=kyek #}VIEWURL <view_file>
Constructs a URL to call on a static file within the view folder. This is extremely useful to link to CSS files, JS files, or images included with the template being used. The following examples assume that the base URL in the config file is "http://example.com" and the view URL given in the autoconfig is "themes/default":
{% viewurl css/main.css %}
{# http://example.com/themes/default/css/main.css #}
{% viewurl /js/jquery.min.js %}
{# http://example.com/themes/default/js/jquery.min.js #}
Note that the 'viewurl' tag does not accept dynamic arguments, so quotes are not required.
Built-in Filters
ADD
Adds the specified number(s) to the variable. Examples:
{% set numOne 2 %}
{% set numTwo 5 %}
2 + 2: {{var|add:2}}
2 + 19 + 20: {{var|add:2:19:20}}
2 + 4 + numTwo: {{var|add:2:4:numTwo}}CAPFIRST
Capitalizes the first letter of a string. Examples:
{% set allLower "i am whispering." %}
{% set allUpper "I AM SCREAMING!" %}
{{allLower|capfirst}} {# Outputs: I am whispering. #}
{{allUpper|lower|capfirst}} {# Outputs: I am screaming! #}DATE
Converts a date string into a specific format. If the date string is relative (like "tomorrow"), the date will be evaluated relative to the current date/time. The format argument follows PHP's date() function (documentation). Example:
{% set dateVar "8 Jan 2011" %}
{{dateVar|date:"N m/d/y"}}
{# Output: Saturday 01/08/11 #}DEFAULT
Provides a default string to display if the variable evaluates to false. Examples:
{# Prints the value of user.name, or "Guest" if user.name is equal to "" or '0' or false. #}
{{user.name|default:"Guest"}}
DIVISIBLEBY
Converts the variable to a true/false value depending on whether or not it is evenly divisible by the provided integer. Example:
{% set num 12 %}
{% set divisor 4 %}
The number {{num}} {{num|divisibleby:divisor|yesno:"is":"is not"}} divisible by {{divisor}}.
{# Output: The number 12 is divisible by 4. #}ESCAPE
Variable tags in Hydrogen are automatically escaped (meaning that < and > are converted to < and >, among other things, to protect you from malicious input). However, using the 'autoescape' block tag covered above, it's possible to turn autoescaping off. In that case, this filter can be used to turn it back on for one specific variable. Example:
{% set htmlText "<b>This is bold!</b>" %}
{% autoescape off %}
{{htmlText}} {# Outputs: <b>This is bold!</b> #}
{{htmlText|escape}} {# Outputs: <b>This is bold!</b> #}
{% endautoescape %}Some filters, like "URLIZE" below, add HTML code to the string, and turn the autoescaping feature off for that one variable automatically in order for the added HTML to not be escaped (in these cases, the filter itself will escape the text before adding its own HTML if autoescaping is still on). The 'escape' filter can be used to force that new HTML to be escaped as well, which is useful if you want to display the added HTML itself on a webpage. Example:
{% set text %}
A link: http://google.com.
{% endset %}
{{text|urlize|escape}}
{# Output: A link: <a href="http://google.com">http://google.com</a>. #}
FILESIZEFORMAT (Contributed by PWNbear)
Formats a number of bytes into a nice human-readable format, followed by "TB", "GB", "MB", "KB", or "bytes". Takes an optional argument specifying how many decimal places to show. If omitted, the default is 2. Note that this will use the real definition of a kilobyte in its calculations, which is 1024 -- not 1000. The result is an accurate file size, and not what you'd get if you simply truncated the number.
All numbers will have trailing zeros after the decimal point removed, regardless of how many decimal places were specified. Examples:
{% set size 45628934585 %}
{{size|filesizeformat}} {# Output: 42.5 GB #}
{{size|filesizeformat:5}} {# Output: 42.49526 GB #}
{{size|filesizeformat:0}} {# Output: 42 GB #}
FIRST
Prints the first element in an array, or the first character of a string. Example:
{# if "someVar" is set to array('a', 'b', 'c'), this outputs 'a': #}
{{someVar|first}}
If the array or string is empty, nothing is output.
JOIN
Joins the elements of an array together using a given string between each element. Example:
{# if "someVar" is set to array('a', 'b', 'c')... #}
{{someVar|join:", "}} {# Outputs: a, b, c #}
LAST
Prints the last element in an array, or the last character of a string. Example:
{# if "someVar" is set to array('a', 'b', 'c'), this outputs 'c': #}
{{someVar|last}}
If the array or string is empty, nothing is output.
LENGTH
Prints the number of elements in an array, or the number of characters in a string. Examples:
{% set name "magik" %}
You have {{name|length}} letters in your name. {# Output: 5 #}
{# Outputs 3 assuming 'posts' is an array of 3 posts: #}
{{posts|length}}LENGTH_IS
Evaluates to true if the length of the array or string is equal to the argument, false otherwise. Example:
{% set name "magik" %}
Your name {{name|length_is:5|yesno:"does":"does not"}} have 5 characters in it.
{# Output: Your name does have 5 characters in it. #}
LINEBREAKS
Converts text separated by blank lines into paragraphs (in <p></p> tags) and newlines inside paragraphs to <br />. Example: If the variable "text" is set to:
This is some text in one variable. How cool is that?
Then this line:
{{text|linebreaks}}...will output this:
<p>This is<br /> some text in<br /> one variable.</p><p>How cool is that?</p>
Lines with no line breaks will still be surrounded in <p></p> tags.
LINEBREAKSBR
Does the same thing as the LINEBREAKS filter, but without paragraph tags. All line breaks are converted to <br />. Example: If the variable "text" is set to:
This is some text in one variable. How cool is that?
Then this line:
{{text|linebreaksbr}}...will output this:
This is<br /> some text in<br /> one variable.<br /> <br /> How cool is that?
LOWER
Converts all letters to lowercase. Example:
{% set greet "Hi, I'm Kyek!" %}
{{greet|lower}} {# Output: hi, i'm kyek! #}MD5
Generates the md5 hash of the variable contents. This is especially useful to hash E-mail addresses to retrieve Gravatars. Example (thanks PWNBear!):
<img class="gravatar" src="http://gravatar.com/avatar/{{user.email|md5}}?s=100" />
PLURALIZE
Makes your words plural or singular based on the value of a numeric variable or the number of entries in an array. Usages:
No Arguments: Will output an "s" if the number/array count is not equal to 1.
One Argument: Will output the arguments if the number/array count is not equal to 1.
Two Arguments: Will output the first argument if the number/array count is equal to 1, or the second argument if it is not.
Examples:
Viewing {{numPosts}} post{{numPosts|pluralize}}.
View our mattress{{mattressList|pluralize:"es"}}.
You have {{cherries|length}} cherr{{cherries|pluralize:"y":"ies"}}.SLUGIFY
Removes all characters that are not alphanumeric or underscores, trims off leading and trailing whitespace, converts the string to lowercase, and replaces spaces with dashes. The result is a string that can be used as a slug in a URL. Example:
{% set postTitle "It's my BLOG post!" %}
{{postTitle|slugify}}
{# Output: its-my-blog-post #}UNESCAPE
Variable tags in Hydrogen are automatically escaped (meaning that < and > are converted to < and >, among other things, to protect you from malicious input). The autoescape tag covered above can turn this off, but this filter will do it for just one variable. Example:
{% set htmlText "<b>This is bold!</b>" %}
{{htmlText}} {# Outputs: <b>This is bold!</b> #}
{{htmlText|unescape}} {# Outputs: <b>This is bold!</b> #}UPPER
Converts all letters to uppercase. Example:
{% set reply "I would not like any of your candy." %}
{{reply|upper}} {# Output: I WOULD NOT LIKE ANY OF YOUR CANDY. #}URLIZE
Converts any URLs found in a string into HTML links. This filter will not check to see if the URL is already part of a link, so this filter is best used on plain text. It will respect autoescaping (if required) without escaping the link code itself, and will not consider trailing punctuation (like a URL followed by a sentence-ending period) to be part of the URL itself. Examples:
{% set text %}
This is a <b>bold word</b> with a link to http://example.com/?search=hello&page=4.
{% endset %}
{{text|urlize}}
{# Output: This is a <b>bold word</b> with a link to <a href="http://example.com/?search=hello&page=4">http://example.com/?search=hello&page=4</a>. #}
{{text|unescape|urlize}}
{# Output: This is a <b>bold word</b> with a link to <a href="http://example.com/?search=hello&page=4">http://example.com/?search=hello&page=4</a>. #}YESNO
Outputs the specified values for boolean variables. Requires two arguments:
- Output for a 'true' value
- Output for a 'false value
If this filter is used with non-boolean variables, all objects will be considered "true", all non-empty strings are true, all empty string ("") are false, all empty arrays are false, and all numbers are true except for 0.
Examples:
Do you like candy? Hell {{likesCandy|yesno:"yeah":"no"}}!
Are you TheEmpty? {{isTheEmpty|yesno:"ye~ah":"Nope"}}.Your Suggestions are Welcome!
It's very easy to write your own additional tags and filters for the Hydrogen templating engine, and there will be a guide for that soon. Until then, if there's a tag or filter that you think would be super useful to everyone who uses this template engine, please don't be shy about suggesting it!
Thanks everyone :D






Cartoon Clouds
Mountains
Sunrise
Clouds
Green Clouds
None




















Help