Willow

Tags

All Willow tags include a matching opening and closing pair, starting and ending with a curly bracket and one other internal character.
Version 1.6.0
Latest Changes:

* New: New loops functionality more stable, more flexible and potentially faster.


Documentation about each release is updated as often as possible, but may lag behind the Master Branch or other generated documentation.

Featured Work
Quinta de Sant’Ana, Portugal
Quinta de Sant’Ana, Portugal

Quinta de Sant'Ana overlooks the picturesque village of Gradil with its cobbled streets, white washed houses and hospitable inhabitants.


Overview

Willow includes a small set of top level tags which can only be used inside .willow file types.

The tag list is as follows – further down this page you can read a short introduction to each tag:

  • Willows {~ ui~hello ~}
  • Variables {{ variable }}
  • Loops {@ loop @}
  • Arguments {+ argument +}
  • Scope {: scope :}
  • Partial {> partial <}
  • Comment {! comment !}

Willow also provides 4 helper tags – in the following formats:

  • PHP Function {@ php_function @}
  • PHP Variable {# php_variable #}
  • Flags [ flag ]
  • Filters [ filters]

Note that both Flags and Filters use the same tag, but are interpreted and used differently.

You can read more about each tag in the sections below.


Willow

Format: {~ [ flags ] ui~hello {+ arguments [ filters ] +} ~}

Willows are the most commonly used tag, along with {{ variables }} and are essentially functions, they accept {+ arguments +} and {@ loops @} or [ flags ]

Take the following example:

{~ ui~hello {+ <div>Willow says {{ [ strtoupper ] hello }}</div> +} ~}

This tag call tries to find the ui ( context ) hello ( task ) method ( ui::hello() ):

Which, if found will return data to Willow, which will apply any [ flags ] and then wrap each data key in the html provided in the {+ arguments +}:

Return:

<div>Willow says HELLO</div>

Note that the data controller returned the value as “Hello”, but the filter strtoupper was applied and transformed the string into uppercase characters.


Variable

Format: {{ [ filters ] variable }}

Variables are “placeholders” which are replaced by the data returned from the calling method, they are usually placed inside “Willow” tags or blocks, they accept [ filters ]

In the following simple example the variable {{ greeting }} is defined and populated with data returned from the class method ui::greeting():

{~ ui~greeting {+ <div>Willow says: {{ [ esc_html ] greeting }}</div> +} ~}

The data returned is modified using  the WordPress escape function esc_html() because the [ esc_html ] filter was defined:

Return:

<div>Willow says: Isn&#039;t it a nice day</div>


Argument

Format: {+ arguments +}

Willow Argument Tags are commonly used to pass configuration options to Tags

Willows can be run without passing arguments, if they collect their configuration from a file, but it is often necessary to pass additional configuration, or, for example to enable debugging via inline arguments – here is an example:

<div class='col-12'>Escaped Value is -> <strong>{~ ui~escaped {+
	[array] markup = 
		template: "{{ [esc_html] escaped }}"
+} ~}</strong></div>

This Willow calls the ui::escaped class method and passes the markup->template to wrap around the returned data value – while also defining that the value of the escaped key should be escaped – via the [escape:html] filter.

Markup

Willow will render returned data values without any markup, however, it is important to note that the default {{ variable }} template will only with a single data->value and is assigned to match the task name.

In both examples, we presume that Willow is returned “good” data, in the format of an array with a single key matching the willow->task name which has a value of “This Worked”, like such:

array(
    'example' => 'This Worked'
)

Here are a few examples to demonstrate how this works:

{~ ui~example ~} will render This Worked

{~ ui~example {+ {{ example }} +} ~} will also render This Worked

{~ ui~example {+ <div>{{ example }}</div> +} ~} will render <div>This Worked</div>

<div class='col-12'>Somehow, <strong>{~ ui~example {+
	[array] markup = 
		template: "{{ example }}"
+} ~}</strong> too :)</div>

will render <div class='col-12'>Somehow, <strong>This Worked</strong> too :)</div>

{~ ui~example {+ {{ bad }} +} ~} will render nothing, as the variable key name does not match

The names of the keys in the returned array determine the variable name to define in the templates for rendering, take the following returned array example:

array(
    'one' => 'I am one',
    'two' => 'I am two',
    'three' => 'I am too.. or three?'
)

This would make available 3 variables within the scope of the current Willow onetwo and three

Configuration

It is also possible to pass simple or complete configuration options from a Willow to the connected class::method, take the following complex example:

{~ group~user_dashboard_featured {+ [array] config = post: {% [return] \willow\get\query::post_id_by_title{+ dashboard +} %} +} ~}

This Willow calls the group context and ask ACF to return data from the field group user_dashboard_featured, while also passing an array of configuration data, including the return value of a PHP Function.

It is possible to pass complex configuration options, but the caveat should be that this comes at the cost of readability, and as we want to provide a tool that is comfortable for UI Developers, it would be good to limit the complexity of passed configuration, where possible.


Loop

Format: {@ loop {: scope :} @}

Willow is a Logic-less template language, meaning that we cannot request data from a controller and then conditionally iterate over it, there are no logical constructs such as if else foreach or while – which are commonplace in most WordPress themes and the cornerstone of the_loop.

Instead, we can loop over data using the Loop tag, which is always used within a {~ willow ~} tag.

Here is a simple example, used to build part of the navigation system for this documents section:

{~ group~docs {+ 
	{@ {: docs_repeater :} 
	<{{ row_header }} id='{{ [ sanitize_title_with_dashes, strtolower ] row_title }}' class='mt-4 mb-2 anchored'>{{ row_title }}</{{ row_header }}>
	{{ row_content }}
	<hr class="my-3" />
	@}
 +} ~}

Loops require an indexed array of data to iterate over, which is defined via the scope tag, in the above example it is declared as {: docs_repeater :}.

The Willow called – without arguments – is {~ group~docs ~} – group contexts pull data from ACF field groups – in this case, a field group called docs – which includes a repeater field called docs_repeater – you can re-use data multiple times within the same Willow and Loop – and within subsequent Willows also, the data object is never modified by filters, only the fields that are rendered.

Loop tags allow the UI developer to define the markup that is returned from the template engine, by defining the start and end points used for data iterations.

Here is another example, from a user system, which runs a request to Advanced Custom Fields – via the group context and loops over the returned indexed array of data in the frontpage_feature key:

<div class='col-12 card-columns'>
	{~ group~frontpage_feature {+ [a] 
		config = debug: false & 
		markup = template: " 
		{@ {: frontpage_feature :}
		<div class='card'>
			<div class='card-body'>
				<h5 class='card-title'>{{ title }}</h5>
				<p class='card-text'>{{ content }}</p>
				<a href='{{ url }}' class='btn btn-primary'>More</a>
			</div>
		</div>
		@}"
	+} ~}
</div>

Notes:

  • Group context requires the Q Plugin to automatically run ACF lookups
  • Loop tags without a scope tag will render nothing
  • Wrapping markup passed in {+ arguments +} tags in “double quotes” will stop the argument parser from removing whitespaces, preserving markup correctly for rendering

Scope

Format: {: scope :}

Scope tags define the data element from a returned data array to increment over and are currently only used inside Loop Tags

Here is an example, from a user system, which runs a request to Advanced Custom Fields – via the group context and loops over the returned indexed array of data in the frontpage_feature key:


<div class='col-12 card-columns'>
	{~ group~frontpage_feature {+ [a] 
		config = debug: false & 
		markup = template: " 
		{@ {: frontpage_feature :}
		<div class='card'>
			<div class='card-body'>
				<h5 class='card-title'>{{ title }}</h5>
				<p class='card-text'>{{ content }}</p>
				<a href='{{ url }}' class='btn btn-primary'>More</a>
			</div>
		</div>
		@}"
	+} ~}
</div>

Notes:
group context requires the Q Plugin to automatically run ACF lookups


Comment

Format: {! [ flag ] comment !}

Comment tag also accept flags, and have three possible use-cases:

HTML Comment

HTML comments renderer inline in the output HTML document

{! [html] This is an HTML comment !}

Returns:

<!-- This is an HTML comment -->

PHP Comment

PHP comments are printed to the PHP debug log, if certain debugging settings conditions are met, if not, they are discarded and removed from the output HTML

{! [php] This is a PHP comment !}

Returns nothing.

Willow Comment

Willow comments are not rendered to HTML or echoed in PHP logs, they are simply used to leave comments in templates.

{! This is an Willow comment !}

Returns nothing

Combining flags

Comments can also be used with multiple flags, the following example would both render an HTML comment and also write to the PHP log file:

{! [html,php] This is an HTML and PHP comment !}

Returns:

<!-- This is an HTML and PHP comment -->

Partial

Format: {> partial <}

Partial tags are perhaps the simplest Willow tag to understand and use, as they are not passed by the parser.

Partials are simply re-usable collections of html or javascript, such as a button or a call to action, they cannot contain other tags and are not parsed by Willow before being rendered.

Partials are stored as text files with a .willow extension – here is an example from one of our Q Themes:

{> search_trigger <}

File Location: library/willow/partial/partial~search_trigger.willow

<div class="row ml-0">
	<div class="col-12 list-group list-group-flush border-top">
		<div class="list-group-item">
			You can also
			<a 
				class="ml-1 btn btn-primary text-white p-2 q-scroll-top" 
				data-toggle="collapse" 
				data-target="#search_content"
				aria-controls="search_content" 
				aria-expanded="false" 
				aria-label="Search navigation"
				href="#">
				Search our Work
			</a>
		</div>
	</div>
</div>

When you call a Partial Willow simply looks for a matching stored configuration file and returns the data, as shown below:

file: q-willow/library/context/partial.php

public static function get( $args = null ) {

	return core\config::get([ 'context' => $args['context'], 'task' => $args['task'] ]);

}

If no matching context/task pair is found, no data will be returned and the partial will render nothing, being removed from the template by the parser cleanup process.

The location of the partial file is important due to how the configuration lookups work – read more


Flag

Format: [ flag ]

Flags are placed within other Willow tags to control the format and usage of returned data.

Currently, Willow only supports only these flags:

  • array -> indicates that configuration is passed in an array format
  • buffer -> tells Willow to attempt to buffer the function response data
  • return -> return data in place of original tag, mostly used with tags
  • php -> used with Willow comment tags to write an php comment
  • html -> used with Willow comment tags to write an html comment

Flags are identified by placing a single flag, or group of flags inside a matching pair of square brackets, the following examples show some of the possible placements and what effect they have:

Array

The example below is the most common usage of flags – using the [array] Array flat to pass a string of configuration options to the defined Willow – also note the use of the [return] flag to pre-build data for the Willow query.

{~ group~frontpage_work {+ [array] config->post = {% [return] get_site_option{+ page_on_front +} %} +} ~}

Return

This example shows the [return] return flag being used to directly replace markup before Willows are run ( as flags are passed first )

<a href="{% [return] get_site_url %}" title="{% [return] get_bloginfo{+ "name" +} %}">
    {% [return] get_bloginfo{+ "name" +} %}
</a>

Buffer

This can be useful where you cannot control the return process of the function being called, but not recommended, note that the flag is on the willow tag, not the variable:

{~ [buffer] ui~needs_buffering {+ <div>{{ variable }}</div> +} ~}

Note:

  • Whole Willows can be filtered using the entire range of available variable filters.

Filter

Format: [ filter ]

Filters are placed within other Willow tags to control the format and usage of returned data.

Flags and Filters are connected concepts and use the same tag: [ filter ].

However, filters are run after Willows are parsed, once the controllers have returned data and Willows have been compiled into HTML, they are used to post-process the data by changing format – here are some examples:

Escape a single property of the returned data array:

{~ ui~example {+ <div>{{ [ esc_html ] needs_escaping }}</div> +} ~}

Remove all html tags from a single property of the returned data array:

{~ ui~example {+ <div>{{ [ strip_tags ] has_tags }}</div> +} ~}

Combine both above filters:

{~ ui~example {+ <div>{{ [ esc_html, strip_tags ] needs_escaping_and_stripping }}</div> +} ~}

Escape entire Willow response – this will include any markup:

Note that all escaping filters use WordPress escape functions – for example esc_html uses esc_html()

{~ [ esc_html ] ui~escape {+ <div>{{ variable }}</div> +} ~}

Passing configuration in a string-formatted array:

<div class='col-12'>Escaped Value is -> <strong>{~ ui~escaped {+
	[array] markup = 
		template: "{{ [ esc_html ] escaped }}"
+} ~}</strong></div>

Notes:

  • Filters are collected as data is buffered in and applied late, before tags are replaced with data.
  • Filters can be extended via the q/willow/filters WordPress filter, which is run once when the first variable is filtered.
  • Variables are filtered first, then then entire tag is filtered by any global filters defined.

PHP Function

Format: {% [ flags ] function %}

Willows are essentially PHP functions wrapped in a few additional layer of sugar, but in some cases, we do not require that sweetening and want to use the raw power of PHP in templates.

PHP functions are parsed earlier that Willows, to enable them to return data to the templates for configuration, they also accept flags.

In order to return data to the template from a PHP function, it is required to add the [return] flag – as shown in the following example:

{~ group~user_dashboard_featured {+ [array] config = post: {% [return] \q\get\query::post_id_by_title{+ dashboard +} %} +} ~}

This would update the original template to something like the following:

{~ group~user_dashboard_featured {+ [array] config = post: 6 +} ~}

In this case, this is useful for a Willow which is shared over multiple templates, but which require the ACF lookup to be run from a specific post ID.

Here are a few more examples, showing how PHP Functions can be used inside Willow templates:

Call an unregistered public PHP class::method, passing an array of data – the [return] flag forces the returned data to render in the output:

<div class="col-12">scope_test_internal: {% [return] \q\theme\context::scope_test_internal{+ [a] a->b = sm:medium, lg:large +} %}</div>

Call a global PHP Function, passing a string, which is rendered as part of the returned data, thanks to the [return] flag:

<div class="col-12">scope_test_external: {% [return] scope_test_external{+ "just a string" +} %}</div>

Load an option from WordPress, passing a string argument:

<div class="col-12">get_site_option->page_on_front: {% [return] get_site_option{+ page_on_front +} %}</div>

Call the WordPress get_the_title() function, passing an integer value:

<h4 class="col-12">get_the_title(): {% [return] get_the_title{+ 6 +} %}</h4>

Notes

  • Willows are designed to supplement PHP functions, it is not realistic to replace them in all use-cases, however, we should attempt to move all logical aspects of data collection, validation and sorting into clean context->task class::methods and only use template files to render and format data.

PHP Variable

Format: {# php_variable #}

PHP Variable are poorly supported and proposed for removal in future releases, currently, only PHP $_GET objects are supported.

It makes more logical sense, and provides greater control to the application developer, that all getting and setting of data be handled inside context methods ( registered class::methods ), rather than making them available to the templates – but this point is still open for discussion 😉

PHP Variables have the following format:

{# php_variable #}

Here is a simple example of getting a value from the PHP GET object – in this case we presume the url of the page is as follows “page.html?message=hello”:

You sent me a "{# message #}"

Which would render in the template

You sent me a "hello"

Notes:

All PHP values are sanitized by the WordPress function sanitize_text_field() before being returned to the template for rendering.