template
The template module contains classes and functions for working with Blade's wire
templates. Wire templating is an extensible template system built on standard HTML5.
In fact, any valid HTML5 document is also a valid Wire template. Wire builds atop
the existing HTML5 language to provide template support for Web development in Blade
and makes extensive use of HTML attributes for condition and looping.
Wire templates allow creation of custom HTML elements (such as the builtin <include />
tag that allows for inlining other template files in a template file.). It's a simple
yet effective system of dynamic programming and the flow altogether feels like writing
in a frontend framework such as VueJS.
Basic Usage
import template
var tpl = template()
echo tpl.render('mytemplate')
Or to render from a string
echo tpl.render_string('<p>{{ name }}</p>', {name: 'Hello World'})
The last example above will render the following:
<p>Hello World</p>
Comments
Wire inherits HTMLs comments using the same syntax <!-- ... -->
. It is important to note
that Wire does not render comments in the output HTML. For web applications, this helps you
to write server side comments in your Wire files without it getting to the frontend since
Wire is backend code.
For example, the code below should return an empty string.
tpl.render_string('<!-- HTML or Wire comment? -->')
Variables
Variables in Wire templates are names surrounded by {{
and }}
pair. For example, to print
the value of a variable myvar passed into [[Template.render]] or [[Template.render_string]]
in the template, you can do it like this.
<div>{{ myvar }}</div>
NOTE:
- The
<div>
and</div>
surround the variable and are not part of the variable.- The spaces around the variable are just formatting and are not required.
The exception to this is when passing a variable to a reserved attribute such as x-key
.
In this case, you'll need to omit the surrounding braces.
For example:
<div x-for="myvar" x-key="mykey"></div>
As shown in the example above, variables can occur anywhere in a Wire template including in element attributes.
To print the exact characters {{ myvar }}
if that's what you actually mean and stop if from
being interpreted as a variable, you'll need to escape the first {
with the percent sign %
.
For example:
<div>%{{ myvar }}</>
The example above will return the following:
<div>{{ myvar }}</div>
Expressions and Modifiers
Expressions in Wire are a feature that allows modification of value directly in the template.
An Expression is value that has been modified by passing it through a functions called
Modifiers using the pipe (|
) character. Wire comes with a lot of built-in functions for
creating expressions and they are all described at below.
For example:
<div>{{ name|length }}</div>
In the example above, the name variable was expressed as its length by passing it into through
the length modifier function. If name contains the value John Doe
, then the value printed
will be 8
.
The built-in modifiers are documentated under Template Functions.
Some expression modifiers require that a value is passed. To pass value to a modifier, use the
equal (=
) sign. For example:
{{ name|is='Jane' }}
In this example, Jane
is a string therefore it is quoted. You can also pass the name of another
variable, a number or any of the constants true
, false
, and nil
directly without the quotes.
For example:
{{ age|is=30.5 }}
If... and If not...
Wire implements conditionals via the x-if
and x-not
attribute that can be attached to any HTML
element. This attributes are never returned in the compiled HTML output and decides wether an
element will be printed or not. The x-if
attribte evaluates a variable or expression and will only
print the element to which it is attached and its children if the result of the expression or variable
evaulation returns a value that is boolean true
in Blade. The x-not
attribute does the reverse of
this (i.e. it only prints if the evaulation returns Blade boolean false
).
tpl.render_string('<div x-if="name">Hello</div>')
The example above will return an empty string since the variable name was never declared. However, the
reverse is the case if the attribute was x-not
.
For example:
tpl.render_string('<div x-not="name">Hello</div>')
The example above will return <div>Hello</div>
.
Loops
Wire templates support for loops is enabled via the x-for
attribute that can be applied to any
element. When the x-for
attribute is present on an element, it must declare a corresponding x-value
attribute that defines the name of the value variable within the loop. An optional x-key
attribute
may also be defined to define a variable name that will contain the value of the iteration index/key.
The x-for attribute must declare a variable or expression that evaluates into an iterable (such as a string, list, dictionary etc.).
For example:
tpl.render_string('<div x-for="data">Ok</div>', {data: 0..3})
The code above will return the following:
<div>Ok</div><div>Ok</div><div>Ok</div>
The original
<div>
was replicated three times without thex-for
attribute. Wire attributes are applied to an element and their children not the children only.
Here is an example using the x-value
attribute to print the items in a list.
tpl.render_string('<div x-for="data" x-value="val">{{ val }}</div>', {data: ['apple', 'mango']})
The code above return
<div>apple</div><div>mango</div>
We could decide to print the index as well by adding a new variable using the x-key
attribute.
tpl.render_string('<div x-for="data" x-value="val" x-key="key">
<span>{{ key }}</span>
<span>{{ value }}</span>
</div>', {data: ['apple', 'mango']})
Which will output
<div></span></span></span></span>
<span></span></span></span></span>0</span></span></span></span>
<span></span></span></span></span>apple</span></span></span></span>
</div></span></span><div></span></span></span></span>
<span></span></span></span></span>1</span></span></span></span>
<span></span></span></span></span>mango</span></span></span></span>
</div></span></span>
Wiring templates
While most of the examples here use the render_string()
function to give a practical approach
to learning Wire templates, the render()
function which allows rendering Wire templates from
files is a more powerful and conventional method of using Wire templates. Not only because they
allow loading templates from files, but also because they allow including other template files in
a template file via the builtin <include />
tag. The include
tag allows wiring multiple Wires
together to create a comprehensive UI layout hierarchy and is quite intuitive to use.
Let's consider a simple use case:
In a website for a client all pages UTF-8 enabled and are mobile first. This leaves room for a set
of <meta>
tags that will need to be on every page of the website and in practice it will soon
become burdersome to have to keep repeating the meta
tags across all page templates. To reduce
this code duplication, we can have a file located at the template root directory (See
[[Template.set_root]]) that contains all shared meta
tags as shown in the sample below and include
this file in every other template.
<!-- templates/meta.html -->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
This template can the be imported in another file with the include
tag.
<!-- templates/layout.html -->
<include path="meta.html" />
The include
tag has only one attribute which is always required and that is the path
attribute.
This attribute allows us to specify the path to a Wire template (or any HTML file for that matter)
that will be rendered in the position the include
tag currently occupies. Take note that in the
example above the path
argument did not start with "templates/". This is because when decoding the
include path, the library first searches for files in the template root directory and if a matching
file is found, that file will be rendered. If none is found, it will interpret the path as a relative
path first then as an absolute path if no match is found.
See [[Tempate.render]] for more information.
Custom Modifiers
Apart from the built-in value modifiers, Wire templates allow you to add custom modifiers in a
simple manner by registering them with the register_function()
method. The example below
shows an example custom modifier reverse that reverses the original value as a string.
tpl.register_function('reverse', @(value) {
return ''.join(to_list(value).reverse())
})
The modifier reverse can then be used in a Wire template like this:
tpl.render_string('<div>{{ fruit|reverse }}</div>', {fruit: 'mango'})
And the output HTML from the above code will be
<div>ognam</div>
Modifier functions can also take a second argument which will recieve any argument passed to the modifier. This is best expressed with an example.
tpl.register_function('reverse_weird', @(value, arg) {
return '${arg}: ' + ''.join(to_list(value).reverse())
})
This above modifier expects an argument that will be used to append the return string. While we acknowledge that this function/modifier is weird, it shows clearly how to create a modifier that takes an argument.
The code below shows how to pass an argument into the reverse_weird
modifier from a template.
<p>{{ fruit|reverse_weird='Reversed' }}</p>
Yes I know. It's weird. But if we passed in the same arguemt as the last, the output will be
<p>Reversed: ognam</p>
Like regular Blade code, the argument will be nil
if not passed and this is
important information if you intend to leverage this for a library that will be used by other
people.
If we remove the argument to the modifier in the template above and simply call
fruit|reverse_weird
, the result will look like this:
<p>: ognam</p>
Custom Tags
As with custom modifiers the template library allows you to create and process custom tags.
An example of a custom tag is the <include />
tag previously discussed. To declare a custom
element and its behavior, you need to create a function that accepts two arguments and
register it with the register_element()
method. When your custom element is matched in a
template, the registered function will be called with an instance of [[Template]] in the first
argument and the {{html}} decoded template as the second argument. Your function must then
return a string representing the processed tag or a valid HTML element Blade representation as
defined by the {{html}} module.
NOTE: It's more memory efficient to modify and return the same element when returing an HTML representation.
The example below defines a custom tag link
that will always be rendered as an anchor
(<a>
) element with the class link
.
tpl.register_element('link', @(this, el) {
return '<a href="${el.attributes[0].value}">${el.attributes[1].value}</a>'
})
The simple tag defined above allows us to process the <link />
tag in a Wire template.
For example,
<link href="bladelang.com" text="Blade Website" />
The Wire template above will cause the following to be rendered.
<a href="bladelang.com">Blade Website</a>
Below is a more complex example that returns an HTML representation in Blade instead of a string.
tpl.register_element('link', @(this, el) {
return {
type: 'element',
name: 'a',
attributes: [
{ name: 'href', value: el.attributes[0].value }
],
children: [
{ type: 'text', content: el.attributes[1].value }
]
}
})
Both code achieve the same thing. However, the later format allows for a more flexible and programmatic output that the former and is the recommended approach wherever possible.
Template Functions
Template functions in Wire are simply modifiers that do not process any value nor accept any argument
(i.e. stand-alone modifiers) and are created in the same way as we create modifiers. However, they are
invoked quite differently. To invoke a template function, you need to wrap them in a {!
and !}
pair.
For example, consider the following template function defined to return the base url of a website.
tpl.register_function('base_url', @{
return 'https://localhost:8000'
})
The function can be invoked as follows:
tpl.render_string('{! base_url !}')
The example above will return https://localhost:8000
.
Like with the {{
and }}
pair for variables, if you really intend to write the {!
and !}
pair,
you'll need to escapte the first {
with a %
sign. For example, {! name !}
will render as
{! name !}
without processing.
Functions
length(value)
Template function to return the length of an iterable.
Example:
{{ value|length }}
If value is Example, output will be 7.
upper(value)
Template function to convert a string or an object's string representation to upper case variant.
Example:
{{ value|upper }}
If value is Example text, output will be EXAMPLE TEXT.
lower(value)
Template function to convert a string or an object's string representation to lower case variant.
Example:
{{ value|lower }}
If value is I'm LOVING this, output will be i'm loving this.
is(value, expected)
Template function to check if object value is same as the expected.
Example:
{{ value|is='Jane' }}
If value was Jane, it will return true
.
You can also pass another variable name, a number or one of true
,
false
, and nil
directly (without quotes).
Parameters
- any expected
not(value, expected)
Template function to check if object value is NOT the same as expected.
Example:
{{ value|not=false }}
If value was true, it will return false
.
It accepts the same set of parameters accepted by the is
template modifier.
Parameters
- any expected
empty(value)
Template function to check if an iterable is empty.
Example:
{{ value|empty }}
If value is an empty string, it will return true
.
reverse(value)
Template function to reverse a string or the string representation of an object.
Example:
{{ value|reverse }}
If value is banana, output will be ananab.
string(value)
Template function to convert an object of any type to a string.
Example:
{{ value|string }}
If value was a list [1,2,3]
, output will be the string [1, 2, 3].
trim(value)
Template function to trim a string.
Example:
{{ value|trim }}
If value is Jane
, output will be Jane
.
title(value)
Template function to convert a string to a title case.
Example:
{{ value|title }}
If value is jane IS a fine girl, output will be Jane Is A Fine Girl.
alt(value, alternative)
Template function to return a default string value if the value passed resolves to a Blade false expression. For example, when a string is empty or nil.
Example:
{{ value|alt=30 }}
If value is -1, out put will be 30.
Parameters
- string alternative
first(value)
Template function to return the first item in an iterable.
Example:
{{ value|first }}
If value is a list ['mango', 'apple', 'oranges']
, output will be mango.
last(value)
Template function to return the last item in an iterable.
Example:
{{ value|last }}
If value is a list ['mango', 'apple', 'oranges']
, output will be oranges.
line_breaks(value)
Template function to replace newlines with HTML line breaks.
Example:
{{ value|line_breaks }}
If value is Hello\nWorld
, output will be Hello<br/>World
.
lpad(value, count)
Template function to left pad a string.
Example:
{{ value|lpad=10 }}
If value is Jane
, output will be Jane
.
Parameters
- number count.
rpad(value, count)
Template function to right pad a string.
Example:
{{ value|rpad=10 }}
If value is Jane
, output will be Jane
.
Parameters
- number count.
join(value, glue)
Template function to join an iterable using a string glue.
Example:
{{ value|join='-' }}
If value is a list ['a', 'b', 'c']
, output will be a-b-c.
Parameters
- string glue
url_encode(value)
Template function to return the url encoded value of a string.
Example:
{{ value|url_encode }}
If value is https://www.example.org/foo?a=b&c=d, output will be https://www.example.org/foo%3Fa%3Db&c%3Dd.
json(value)
Template string to return the JSON encoded string for a value.
Example:
{{ value|json }}
If value is a dictionary {name: 'Xavier'}
, output will be {"name":"Xavier"}.
template(auto_init) ⇢ Exported
Default function exporting the [[Template]] class that allows function initialization. See [[Template]].
Parameters
- bool auto_init
Classes
class Template
Template string and file processing class.
Usage
You can render templates directly from strings
import template
var tpl = template()
tpl.render_string('{{ name }}', {name: 'John Doe'})
Or from files located in your defined root directory. See [[Template.set_root]]
tpl.render('my_template', {name: 'John Doe'})
You can enable initialize your templates with the auto_init option to allow
[[Template]] create the root directory if it does not exist. The default root
directory is a directory "templates
" in the current working directory.
For example,
var tpl = template(true)
# Optionally set the root directory to another directory.
tpl.set_root('/my/custom/path')
The root directory will become the root search path for the <include />
tag.
The default extension for a template file is the .html
extension. This extension
allows furnishes the interopability between Blade's Wire templates and HTML5 since the
former is based on the later anyway and allows us to leverage the already near
omnipresent support that HTML files have had over the years. This behavior can be
changed using the [[Template.set_extension]] function to change the extension to any
desired string.
For example,
tpl.set_extension('.wire')
# render a template from file
tpl.render('welcome')
This will cause [[Template.render]] to look for the file "welcome.wire
" in the root
directory and will return an error if the file could not be found and no file matches
exactly "welcome
" in the directory.
Methods
Template(auto_init) ⇢ Constructor
The constructor of the Template class.
directory will be automatically created on [[Template.set_root]] or [[Template.render]].
Parameters
- bool auto_init: : A boolean flag to control whether template root
set_root(path)
Set the template files root directory for [[Template.render]]. Returns true
if
the directory was automatically created (See [[Template._auto_init]]) or false
if it wasn't.
If your template contains or will contain an <include />
tag, the path given
here will become the root of the include search path.
Parameters
- string path
Returns
- bool
set_extension(ext)
Sets the default file extension to be used when [[Template.render]] and/or the
<include />
tag searches for template files in the root directory when the path
given does not match an existing file and does not end with another extension.
Parameters
- string ext
register_function(name, function)
Registers a function that can be used to process variables in the template. The given function must accept a minimum of one argument which will recieve the value of the value to be processed and at most two arguments, the second of which will recieve arguments passed to the function as a string.
Example
def firstname_function(value) {
return value.split(' ')[0]
}
tpl.register_function('firstname', firstname_function)
The registered function can be used in the template to process variables. For example,
<div>{{ my_user|firstname }}</div>
Parameters
- string name
- function function
register_element(name, element)
Registers a custom HTML element for the template. The function passed must take exactly two (2) arguments the first of which will recieve the the template object iteself and the second the HTML as an object of {{html}}.
Example
def inline_input(wire, value) {
return ...
}
tpl.register_element('inline-input', my_custom_function)
The above registered element can then be used in the template. For example,
<inline-input value="{{ my_var }}" />
Parameters
- string name
- function(2) element
render_string(source, variables, path)
Process and render template contained in the given string. The variables dictionary is used to pass variable data to the template being processed.
If a variable is required in the template and is missing in the variables
dictionary or the variables dictionary was not passed to the render_string()
call, the process dies with an Exception. The third argument allows specifying
the source file/path of the template being processed and will default to
<source>
when not passed.
The path argument may be of important if the string was read from a file or a similar source to provide information on the source of wrong template data such as line and column information.
Example
tpl.render_string('<div>{{ name }}</div>', {name: 'Johnson'})
The above template should return
<div>Johnson</div>
Parameters
- string source
- dict? variables
- string? path
Returns
- string
render(path, variables)
Process and render template contained in the given template file. The template
path should be a path relative to the root directory (See [[Template]]) and may
or not carry any extension. If the template file uses the template extension
(defualt: .html
), the path argument may exlcude the extension from the path
altogether provided there is a file with a matching name that may or not have the
default extension (See [[Template.set_extension]]).
The variables dictionary is used to pass variable data to the template being processed and behaves exactly the same way as with [[Template.render_string]].
Example
tpl.render('my_template')
The above example renders the template as is and will raise if any variable is found in it. You can pass a variable the same way you do with [[Template.render_string]].
Parameters
- string path
- dict? variables
Returns
- string