Skip to content

Advanced Guide

Nana Axel edited this page Jan 9, 2019 · 22 revisions

Summary


AirBubble configuration

Before create bubbles, you can change the behavior of the compiler by changing values of the AirBubbleConfigurator. Theses values will be used by all instances of the compiler. There are some configuration values:

  • indentOutput: Define if the compiled output have to be indented. Default value is true
  • templateBasePath: The base path of templates. This is useful when creating abstract templates because the compiler search included templates from this path. Default value is ./
  • templateExtension: The extension to use when resolving template paths without extension. Default value is bubble
  • encoding: The character encoding to use. Default value is utf-8
  • leaveComments: Define if comments must be removed in the compiled output or not. Default value is false

To configure AirBubble, you just have to create a new instance of the configurator, define values, and call the static method \ElementaryFramework\AirBubble\AirBubble::setConfiguration() with your config.

<?php

use \ElementaryFramework\AirBubble\AirBubble;
use \ElementaryFramework\AirBubble\AirBubbleConfig;

// Create a configuration
$config = (new AirBubbleConfig())
  ->setIndentOutput(false)
  ->setLeaveComments(true)
  ->setTemplateBasePath(realpath(__DIR__ . "/app/templates/"));

// Use the configuration for all AirBubble instances
AirBubble::setConfiguration($config);

Data binding

With AirBubble, you are able to bind data in templates from PHP. To do this, you have to use \ElementaryFramework\AirBubble\AirBubble::set(string, object).

  • The first parameter is a string, which represent the variable name of the data in the template.
  • The second parameter is an object, which represent the value of the data.

The second parameter can be of all primitive types in PHP (string, number, array, boolean, null, etc...) or (in the case of an user-defined type) a class which implements \ElementaryFramework\AirBubble\Data\IAirBubbleDataContext.

To use the data sent from PHP in the template, you have to write ${variable_name} where you want to use your value (variable_name represents the name you pass as the first parameter of \ElementaryFramework\AirBubble\AirBubble::set())

Example of primitive type data binding

test.bubble: Template

<!DOCTYPE html>
<b:bubble xmlns:b="http://bubble.na2axl.tk/schema">
  <html lang="en">

    <head>
      <meta charset="UTF-8"/>
      <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
      <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
      <title>AirBubble Template Test</title>
    </head>

    <body>
      <b:text value="${hello}"/>
      <br/>
      With AirBubble, you can pass to the template all supported primitive types of PHP!
      <ul>
        <li>String: ${string}</li>
        <li>Number: ${number}</li>
        <li>Boolean: ${bool}</li>
        <li>Array: ${array[0]}, ${array[1]} !</li>
        <li>Associative array: ${associative.foo}, ${associative.bar} !</li>
        <li>Null: ${null}</li>
      </ul>
    </body>

  </html>
</b:bubble>

test.php: Compiler

<?php

use \ElementaryFramework\AirBubble\AirBubble;

// Create a new instance of the compiler
$bubble = new AirBubble();

// Send data to template
$bubble->set("hello", "Hello, World !");
$bubble->set("string", "This is a string sent to the template");
$bubble->set("number", "2627");
$bubble->set("bool", true);
$bubble->set("array", array("Hello", "World"));
$bubble->set("associative", array("foo" => "Hello", "bar" => "World"));
$bubble->set("null", null);

// Compiles the template file and write the output in another
$bubble->compileFile("test.bubble", "test.html");

// Compiles the template file and return the output as string
echo $bubble->renderFile("test.bubble");

test.html: Compiled output

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>AirBubble Template Test</title>
  </head>

  <body>
    Hello, World ! <br />
    With AirBubble, you can pass to the template all supported primitive types
    of PHP!
    <ul>
      <li>String: This is a string sent to the template</li>
      <li>Number: 2627</li>
      <li>Boolean: true</li>
      <li>Array: Hello, World !</li>
      <li>Associative array: Hello, World !</li>
      <li>Null:</li>
    </ul>
  </body>
</html>

NOTE: The value of null is not displayed.

Example of data binding with data context

test.bubble: Template

<!DOCTYPE html>
<b:bubble xmlns:b="http://bubble.na2axl.tk/schema">
  <html lang="en">

    <head>
      <meta charset="UTF-8"/>
      <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
      <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
      <title>AirBubble Template Test</title>
    </head>

    <body>
      <b:text value="${hello}"/>
      <br/>
      With AirBubble, you can pass to the template a data context, which allows you to use all public members (methods and properties) of a class in your template!
      <ul>
        <li>Is ${username} ?: ${context.isUser(${username})}</li>
        <li>Username: ${context.data.username}</li>
        <li>Email: ${context.data.email}</li>
        <li>Activated: ${context.isActivated()}</li>
        <li>Banished: ${context.isBanished()}</li>
      </ul>
    </body>

  </html>
</b:bubble>

user.php: Data context

<?php

use \ElementaryFramework\AirBubble\Data\IAirBubbleDataContext;

class User implements IAirBubbleDataContext {

    public $data = array(
        "id" => 1,
        "username" => "na2axl",
        "password" => "1234567890",
        "email" => "ax.lnana@outlook.com",
        "banished" => false,
        "activated" => true,
        "administrator" => true
    );

    public function isAdmin() {
        return $this->data["administrator"];
    }

    public function isBanished() {
        return $this->data["banished"];
    }

    public function isActivated() {
        return $this->data["activated"];
    }

    public function isUser($username) {
        return $this->data["username"] === $username;
    }
}

test.php: Compiler

<?php

// Require the data context
require_once "user.php";

use \ElementaryFramework\AirBubble\AirBubble;

// Create a new instance of the compiler
$bubble = new AirBubble();

// Send data to template
$bubble->set("hello", "Hello, World !");
$bubble->set("username", "whitney");
$bubble->set("context", new User());

// Compiles the template file and write the output in another
$bubble->compileFile("test.bubble", "test.html");

// Compiles the template file and return the output as string
echo $bubble->renderFile("test.bubble");

test.html: Compiled output

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>AirBubble Template Test</title>
  </head>
  <body>
    Hello, World ! <br />
    With AirBubble, you can pass to the template a data context, which allows
    you to use all public members (methods and properties) of a class in your
    template!
    <ul>
      <li>Is whitney ?: false</li>
      <li>Username: na2axl</li>
      <li>Email: ax.lnana@outlook.com</li>
      <li>Activated: true</li>
      <li>Banished: false</li>
    </ul>
  </body>
</html>

Example of data binding with dynamic data context

test.bubble: Template

<!DOCTYPE html>
<b:bubble xmlns:b="http://bubble.na2axl.tk/schema">
  <html lang="en">

    <head>
      <meta charset="UTF-8"/>
      <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
      <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
      <title>AirBubble Template Test</title>
    </head>

    <body>
      <b:text value="${hello}"/>
      <br/>
      With AirBubble, you can pass to the template a <b>dynamic</b> data context, which allows you to use all public members (methods and properties) of a class in your template, and also access properties and call methods which are not defined in this class !
      <ul>
        <!-- Call dynamic method "is()" -->
        <li>Is ${username} ?: ${dynamic.is(${username})}</li>
        <!-- Access dynamic properties -->
        <li>Username: ${dynamic.username}</li>
        <li>Email: ${dynamic.email}</li>
        <li>Activated: ${dynamic.activated}</li>
        <li>Banished: ${dynamic.banished}</li>
      </ul>
    </body>

  </html>
</b:bubble>

user.php: Data context

<?php

use \ElementaryFramework\AirBubble\Data\IAirBubbleDynamicDataContext;

class User implements IAirBubbleDynamicDataContext {

    private $_data = array(
        "id" => 1,
        "username" => "na2axl",
        "password" => "1234567890",
        "email" => "ax.lnana@outlook.com",
        "banished" => false,
        "activated" => true,
        "administrator" => true
    );

    /**
     * Method called when the DataResolver try to find a dynamic property.
     *
     * @param string $name The name of the dynamic property.
     *
     * @return mixed
     */
    public function getBubbleProperty(string $name)
    {
        return $this->_data[$name];
    }

    /**
     * Method called when the DataResolver try to find an indexed dynamic property.
     *
     * @param string $name  The name of the dynamic indexed property.
     * @param mixed  $index The index to access in the property.
     *
     * @return mixed
     */
    public function getBubbleIndexedProperty(string $name, $index)
    {
        return null;
    }

    /**
     * Method called when the DataResolver try to find a dynamic method.
     *
     * @param string $name       The name of the dynamic method.
     * @param array  $parameters The set of parameters of the dynamic method.
     *
     * @return mixed
     */
    public function callBubbleMethod(string $name, array $parameters)
    {
        switch ($name)
        {
            case "is":
                return $this->_data["username"] === $parameters[0];

            // More dynamic methods can come here...
        }

        return null;
    }
}

test.php: Compiler

<?php

// Require the dynamic data context
require_once "user.php";

use \ElementaryFramework\AirBubble\AirBubble;

// Create a new instance of the compiler
$bubble = new AirBubble();

// Send data to template
$bubble->set("hello", "Hello, World !");
$bubble->set("username", "whitney");
$bubble->set("context", new User());

// Compiles the template file and write the output in another
$bubble->compileFile("test.bubble", "test.html");

// Compiles the template file and return the output as string
echo $bubble->renderFile("test.bubble");

test.html: Compiled output

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>AirBubble Template Test</title>
  </head>
  <body>
    Hello, World ! <br />
    With AirBubble, you can pass to the template a <b>dynamic</b> data context,
    which allows you to use all public members (methods and properties) of a
    class in your template, and also access properties and call methods which
    are not defined in this class !
    <ul>
      <li>Is whitney ?: false</li>
      <li>Username: na2axl</li>
      <li>Email: ax.lnana@outlook.com</li>
      <li>Activated: true</li>
      <li>Banished: false</li>
    </ul>
  </body>
</html>

Dynamically assign variables in the template

It is possible to directly assign variables into the template. The b:assign tag is used for these cases.

For example, the template code:

<b:assign var="foo" value="Hello" />
<b:assign var="baz" value="World" />
<p> This text is rendered from dynamically assigned variables: "${foo}, ${baz} !" </p>

will render:

<p> This text is rendered from dynamically assigned variables: "Hello, World !" </p>

NOTE: The b:assign tag renders nothing on the compiled HTML.

The b:assign tag is also capable to edit the value of an externally declared variable. For example:

$bubble->set("age", 21);
<p> You have ${age} years old ! </p>
<b:assign var="age" value="12" />
<p> You have 12 years old ! </p>

NOTE: The b:assign tag is a PRE_PARSE_TOKEN type, so it's parsed before any other token, without taking care of the position in the DOM.

Functions

In AirBubble templates you are able to call native PHP functions and built-in functions. Every calls must be wrapped in double braces ({{ and }}). All of built-in AirBubble functions must start with an @ to create a difference with native PHP functions.

Example

test.bubble: Template

<!DOCTYPE html>
<b:bubble xmlns:b="http://bubble.na2axl.tk/schema">
  <html lang="en">

    <head>
      <meta charset="UTF-8"/>
      <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
      <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
      <title>AirBubble Template Test</title>
    </head>

    <body>
      <b:text value="{{ strtoupper(${hello}) }}"/>
      <br/>
      With AirBubble, you can call native PHP functions or built-in functions in your template. All you need is to use <b>double braces</b> to wrap your call in!
      <br/>
      <b:text element="p" value="{{ @truncate(${long_text}, 30) }}" />
      The previous (very) long text have {{ strlen(${long_text}) }} characters and have been certainly truncated in 30 characters.
    </body>

  </html>
</b:bubble>

test.php: Compiler

<?php

use \ElementaryFramework\AirBubble\AirBubble;

// Create a new instance of the compiler
$bubble = new AirBubble();

// Send data to template
$bubble->set("hello", "Hello, World !");
$bubble->set("long_text", "Qui aliquip excepteur qui aliquip aliqua. Quis eiusmod ad duis pariatur veniam mollit pariatur sunt velit ea dolor non ex cupidatat. Laboris occaecat cupidatat esse aliqua sit mollit mollit consectetur officia eiusmod minim ut minim. Tempor excepteur esse ad officia fugiat qui veniam. Ex sunt ut elit consequat. Ea do aute ea sunt. Est pariatur fugiat ullamco eiusmod cupidatat duis duis excepteur voluptate qui. Consequat ullamco consectetur id labore.");

// Compiles the template file and write the output in another
$bubble->compileFile("test.bubble", "test.html");

// Compiles the template file and return the output as string
echo $bubble->renderFile("test.bubble");

test.html: Compiled output

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>AirBubble Template Test</title>
  </head>
  <body>
    HELLO, WORLD ! <br />
    With AirBubble, you can call native PHP functions or built-in functions in
    your template. All you need is to use <b>double braces</b> to wrap your call
    in! <br />
    <p>Qui aliquip excepteur qui a...</p>
    The previous (very) long text have 454 characters and have been certainly
    truncated in 30 characters.
  </body>
</html>

Bubble functions list

  • @isset(string var_name): Check if var_name is a defined template variable.
  • @upper(string text): Transform your text in uppercase.
  • @lower(string text): Transform your text in lowercase.
  • @capitalize(string text): Capitalize the first letter of your text.
  • @spacify(string text, string space = " "): Insert space (or optionally a given string) between all characters of the text.
  • @strip(string text, string space = " "): Replaces multiple spaces, new lines and tabulations by a space (or optionally a given string).
  • @truncate(string text, int length = 80, string trunk = "...", bool trunkWord = false, bool trunkMiddle = false): Truncates a string to the given length.
    • text: The string to truncate.
    • length: The truncated string length.
    • trunk: The string to insert at the truncated position.
    • trunkWord: Define if we break words (false) or not (true).
    • trunkMiddle: Define if we truncate at the middle of the string (true) or not (false).

Functions context

All template functions are found in a FunctionsContext. The default context of AirBubble is \ElementaryFramework\AirBubble\Data\FunctionsContext, and contains all default bubble's functions. You are able to create your own context to call custom functions in your templates, you just have to create a new class which extends the default AirBubble's functions context.

CustomFunctionsContext.php: Custom functions context

<?php

use \ElementaryFramework\AirBubble\Data\FunctionsContext;

class CustomFunctionsContext extends FunctionsContext
{
    public function replaceSpacesWithDashes($string)
    {
        return str_replace(" ", "-", $this->lower($string));
    }
}

test.bubble: Template

<!DOCTYPE html>
<b:bubble xmlns:b="http://bubble.na2axl.tk/schema">
  <html lang="en">

    <head>
      <meta charset="UTF-8"/>
      <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
      <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
      <title>AirBubble Template Test</title>
    </head>

    <body>
      <b:text value="{{ strtoupper(${hello}) }}"/>
      <br/>
      With AirBubble, you can call native PHP functions, built-in functions, or your custom set of functions in your template. All you need is to use <b>double braces</b> to wrap your call in!
      <br/>
      <b:text element="p" value="{{ @replaceSpacesWithDashes(@truncate(${long_text}, 30)) }}" />
      The previous (very) long text have {{ strlen(${long_text}) }} characters and have been certainly truncated in 30 characters, transformed in lowercase, and all spaces become dashes.
    </body>

  </html>
</b:bubble>

test.php: Compiler

<?php

require_once "CustomFunctionsContext.php";

use \ElementaryFramework\AirBubble\AirBubble;
use \ElementaryFramework\AirBubble\Util\EvalSandBox;

// We define the custom functions context
EvalSandBox::setFunctionsContext(CustomFunctionsContext::class);

// Create a new instance of the compiler
$bubble = new AirBubble();

// Send data to template
$bubble->set("hello", "Hello, World !");
$bubble->set("long_text", "Qui aliquip excepteur qui aliquip aliqua. Quis eiusmod ad duis pariatur veniam mollit pariatur sunt velit ea dolor non ex cupidatat. Laboris occaecat cupidatat esse aliqua sit mollit mollit consectetur officia eiusmod minim ut minim. Tempor excepteur esse ad officia fugiat qui veniam. Ex sunt ut elit consequat. Ea do aute ea sunt. Est pariatur fugiat ullamco eiusmod cupidatat duis duis excepteur voluptate qui. Consequat ullamco consectetur id labore.");

// Compiles the template file and write the output in another
$bubble->compileFile("test.bubble", "test.html");

// Compiles the template file and return the output as string
echo $bubble->renderFile("test.bubble");

test.html: Compiled output

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>AirBubble Template Test</title>
  </head>
  <body>
    HELLO, WORLD ! <br />
    With AirBubble, you can call native PHP functions, built-in functions, or
    your custom set of functions in your template. All you need is to use
    <b>double braces</b> to wrap your call in! <br />
    <p>qui-aliquip-excepteur-qui-a...</p>
    The previous (very) long text have 454 characters and have been certainly
    truncated in 30 characters, transformed in lowercase, and all spaces become
    dashes.
  </body>
</html>

Conditions

Some times you need to render a specific part of your template and hide another, depending on a certain condition. In AirBubble, you are able to do this by using the b:condition tag.

<b:condition>
  <if condition="...">
    <b:text value="Text rendered if this condition is satified." />
  </if>
  <elseif condition="...">
    <b:text value="Text rendered if this condition is satified." />
  </elseif>
  <!-- Any other elseif cases you want... -->
  <else>
    <b:text value="Text rendered when no condition is satisfied." />
  </else>
</b:condition>

The value of the condition attribute must be a boolean, which can be strongly typed (eg: condition="{{ true }}"), a template variable (eg: condition="${userIsConnected}"), a data context method or property (eg: condition="${sessionManager.isActiveSession()}"), or a function call (eg: condition="{{ file_exists(${user.avatarPath}) }}").

Inline conditions

Inline conditions are useful when we want to change the state or a simple value according to a single condition. In AirBubble, you create an inline condition with the double braces notation:

  <button id="{{ ${sessionManager.isActiveSession()} ? 'logoutBtn' : 'loginBtn' }}">
    {{ ${sessionManager.isActiveSession()} ? 'Logout' : 'Login' }}
  </button>

Loops

In AirBubble template you can create two kind of loops, for loops and foreach loops.

for loop

A for loop allow you to repeat a block rendering for a given number of time. The syntax of the for loop is:

<b:for var="variable_name" from="start_number" to="end_number">
  <!-- Your content -->
</b:for>

Examples

Example 1: Basic example

<b:for var="i" from="1" to="5">
  <b:text element="p" value="Iteration ${i}: Hello, World !" />
</b:for>

Example 2: Create a ul list from array:

$bubble->set("list", array("Item 1", "Item 2", "Item 3", "Item 4"));
$bubble->set("nb_items" , count($bubble->get("list")));
<ul>
  <b:for var="i" from="0" to="{{ ${nb_items} - 1 }}">
    <b:text element="li" value="${list[${i}]}" />
  </b:for>
</ul>

OR

<ul>
  <b:for var="i" from="0" to="{{ ${nb_items} - 1 }}">
    <b:text element="li" value="${list.${i}}" />
  </b:for>
</ul>

NOTE: from and to attributes values are inclusive.

foreach loop

A foreach loop allows you to pass through each values of an iterable object. The syntax of the foreach loop is:

<b:foreach value="iterable value" key="variable_name" var="variable_name">
  <!-- Your content -->
</b:foreach>

Examples

Example 1: Building a list

$bubble->set("listItems", array("Item 1", "Item 2", "Item 3", "Item 4"));
<ol>
  <b:foreach value="${listItems}" var="item">
    <li>${item}</li>
  </b:foreach>
</ol>

NOTE: The key attribute is optional. Only value and var attributes are mandatory.

Example 2: Select dropbox

$bubble->set("userOptions", array("opt1" => "Option 1", "opt12" => "Option 2", "opt3" => "Option 3", "opt4" => "Option 4"));
<select>
  <b:foreach value="${userOptions}" key="option_id" var="option_name">
    <option value="${option_id}">${option_name}</option>
  </b:foreach>
</select>

Data tables

Data tables allows you to arrange a set of data of the same type in an HTML table, without using a loop. You just have to define your table structures, setting columns, headers, footers, etc and let AirBubble do the magic!

<b:dataTable value="iterable_value" var="variable_name" key="variable_name">

  <!-- Add a column to the table -->
  <column>
    <head>
      <!-- Column header content -->
    </head>
    <content>
      <!-- Column body content -->
    </content>
    <foot>
      <!-- Column footer content -->
    </foot>
  </column>

  <!-- Add the number of columns you want -->

</b:dataTable>

You create a new data table with the b:dataTable tag. AirBubble data tables are managed with columns, not rows, so to add a new column to the table you use the column tag. Inside columns, you can define the column header with the head tag, the column body with the content tag and the column footer with the foot tag.

Notes

  • The b:dataTable tag must have at least one column tag as child
  • The b:dataTable tag can only contains column tags as children
  • The column tag must have only one content tag in the child list
  • The head tag is optional in column tags, and must be unique if present
  • The foot tag is optional in column tags, and must be unique if present

Examples

Example: Show a list of registered users

<?php

use \ElementaryFramework\AirBubble\Data\IAirBubbleDataContext;

class User implements IAirBubbleDataContext {

    public $data = array(
        "id" => 1,
        "username" => "na2axl",
        "password" => "1234567890",
        "email" => "ax.lnana@outlook.com",
        "banished" => false,
        "activated" => true,
        "administrator" => true
    );

    public function __construct(?array $data = null) {
      if ($data !== null) {
        $this->data = $data;
      }
    }

    public function isAdmin() {
        return $this->data["administrator"];
    }

    public function isBanished() {
        return $this->data["banished"];
    }

    public function isActivated() {
        return $this->data["activated"];
    }

    public function isUser($username) {
        return $this->data["username"] === $username;
    }
}
// Retrieve the list of users from the database...
// Store the list in $rawList...

$usersList = array();

// Generate the array of User objects
foreach ($rawList as $rawUser) {
  array_push($usersList, new User($rawUser));
}

// Send the array to the template
$bubble->set("usersList", $usersList);
<!DOCTYPE html>
<b:bubble xmlns:b="http://bubble.na2axl.tk/schema">
  <html lang="en">

    <head>
      <meta charset="UTF-8"/>
      <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
      <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
      <title>AirBubble Data Table Test</title>
    </head>

    <body>
      <b:dataTable value="${usersList}" var="user" key="i">
        <column>
          <head>
            <b:text element="b" value="#" />
          </head>
          <content>
            <!-- The key is zero-based, so we add 1 to simulate a row counter. -->
            <b:text value="{{ ${i} + 1 }}" />
          </content>
        </column>

        <column>
          <head>
            <b:text element="b" value="Username" />
          </head>
          <content>
            <b:text value="${user.data.username}" />
          </content>
        </column>

        <column>
          <head>
            <b:text element="b" value="Email" />
          </head>
          <content>
            <b:text value="${user.data.email}" />
          </content>
        </column>
      </b:dataTable>
    </body>

  </html>
</b:bubble>

Template parts

With AirBubble, you can split a template file into multiple parts, and reuse these parts in another files. You just have to use the b:include tag to include a template into another.

<b:include path="path_to_template_to_include" />

Example

navbar.bubble

<b:bubble xmlns="http://bubble.na2axl.tk/schema">
  <header>
    <nav>
      <ul>
        <b:foreach value="${navMenuList}" var="menu">
          <li class="{{ @lower(${current_page}) === @lower(${menu}) ? 'active' : 'normal' }}" >${menu}</li>
        </b:foreach>
      </ul>
    </nav>
  </header>
</b:bubble>

footer.bubble

<b:bubble xmlns="http://bubble.na2axl.tk/schema">
  <footer>
    <div>
      <b:text element="p" style="text-align: center;" value="Copyright &copy; ${appName}" />>
    </div>
  </footer>
</b:bubble>

home.bubble

<b:bubble xmlns="http://bubble.na2axl.tk/schema">
  <html lang="en">

    <head>
      <meta charset="UTF-8"/>
      <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
      <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
      <title>AirBubble Template Test</title>
    </head>

    <body>
      <!-- This will automatically search for a template file with name navbar, with the extension defined in AirBubbleConfig, starting at the templates base path defined in AirBubbleConfig -->
      <b:include path="navbar" current_page="Home" />
      <main>
        Welcome to the Home page !
      </main>
      <!-- Absolute and relative paths are also supported -->
      <b:include path="./footer.bubble" />
    </body>

  </html>
</b:bubble>
$bubble->set("navMenuList", array("Home", "About", "Contact"));
$bubble->set("appName", "My awesome portfolio");

REMARKS: Using the b:include tag, only the path attribute is mandatory, all other attributes will be considered as template part variables declarations. In the previous example, we define navMenuList and appName as data used to render the template file home.bubble which includes header.bubble and footer.bubble. But, header.bubble use a third data binding value (current_page), this value is defined in home.bubble at the inclusion time (<b:include path="navbar" current_page="Home" />) by the current_page attribute. By this way, you have the possibility to change the behavior of a template parts according to the file which includes them.

Note: When overwriting template parts variables with attributes at the inclusion time, the value defined in the AirBubble data model, if any, is also overwritten, only for the concerned template part.

Abstract templates

Abstract templates are template files which have to be inherited to be properly rendered. You can use abstract templates to create a base structure for another (eg. the HTML structure of all pages of your site, the base structure of some kind of posts of your blog (image posts, video posts, etc)).

A template become abstract when he contains at least one b:block tag:

<b:block name="block_name" />

Note: When rendering abstract templates directly, the b:block tag renders nothing.

At this this time, the abstract template can now be inherited by other template files to override the set of b:block tags:

<b:bubble xmlns:b="http://bubble.na2axl.tk/schema" extends="path_to_the_abstract_template">
  <block_name>
    <!-- Content overriding the block in abstract template -->
  </block_name>
</bubble>

The extends attribute is mandatory when overriding abstract template, its value is the path to the template file to inherit from. In concrete templates, the child list of the b:bubble tag must be tags with names defined by the set of b:block tags in the inherited abstract template, no other tags are allowed.

Example

Example 1: HTML website base structure

base.bubble: Abstract template

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<b:bubble xmlns:b="http://bubble.na2axl.tk/schema">
  <html lang="en">
    <head>
      <meta charset="utf-8"/>
      <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
      <title>${page_title}</title>
      <meta name="description" content="${page_description}" />
      <meta name="viewport" content="width=device-width, initial-scale=1" />
      <meta name="robots" content="all,follow" />
      <link rel="stylesheet" href="/assets/css/g.r.i.d.css" />
      <link rel="stylesheet" href="/assets/css/main.css" />
      <b:block name="styles" />
    </head>
    <body>
      <b:block name="content" />
      <script src="/assets/js/jquery.js" />
      <script src="/assets/js/g.r.i.d.js" />
      <script src="/assets/js/main.js" />
      <b:block name="scripts" />
    </body>
  </html>
</b:bubble>

home.bubble: Concrete template

<b:bubble xmlns:b="http://bubble.na2axl.tk/schema" extends="base">
  <styles>
    <link rel="stylesheet" href="/assets/css/home.css" />
    <link rel="stylesheet" href="/assets/css/carousel.css" />
  </styles>

  <content>
    <div>
      My awesome home page content, with carousels, sliders, charts and other cool stuffs !
    </div>
  <content>

  <scripts>
    <link rel="stylesheet" href="/assets/css/carousel.js" />
    <link rel="stylesheet" href="/assets/css/home.js" />
  </scripts>
</b:bubble>

Note: The order of overridden blocks in concrete templates is independent of the declaration order in the abstract template.

What next?