-
-
Notifications
You must be signed in to change notification settings - Fork 4
Binding
One of the most common usages of DomTemplate is to bind data to the document - to inject information into elements to make pages dynamic. DomTemplate uses data-bind
and data-list
attributes on HTML elements to indicate what, where and how to bind data.
All examples in this section assume an HTMLDocument
object is construced and passed to the DocumentBinder
constructor. Learn more about constructing DomTemplate objects.
Run all examples in this section locally: https://github.com/PhpGt/DomTemplate/tree/master/examples/binding
On any element that you wish to bind data to, a bind attribute can be set that is made up of the following:
- the attribute name starting with
data-bind
- followed by a colon
- followed by the bind property - what
HTMLElement
property to bind to - optionally, a bind key can be supplied after an equals sign (
=
)
Some examples, to help visualise the different parts of a data bind attribute:
-
<span data-bind:text="name">Your name</span>
- thetext
bind property will be bound with the value of thename
bind key. -
<img src="/default.svg" alt="Profile image" data-bind:src="profileImageUrl" data-bind:alt="fullName" />
- thesrc
bind property will be bound with the value of theprofileImageUrl
bind key, and thealt
bind property will be bound with the value of thefullName
bind key. -
<button name="do" value="delete" data-bind:class="selected">Delete</button>
- theclass
bind property will addselected
to the element'sclass
attribute if the value of theselected
bind key istrue
or truthy. Theclass
bind property has special
In a data-bind
attribute, the "bind property" is what appears after the colon in the element's attribute name. So in the example data-bind:href="exampleUrl"
, the bind property is href
.
The bind property is used to indicate what property of the HTMLElement
should have dynamic data bound to it. Any property can be used, such as href
, src
, alt
, title
, etc. and even other data attributes (data-bind:data-id="id"
will set the data-id
attribute). There are also some bind properties that have special behaviour, like when you want to toggle a value within the class
attribute.
Here is a list of the bind properties that have special behaviour:
-
text
,inner-text
,innertext
,text-content
andtextcontent
are synonyms that will set thetextContent
property of theHTMLElement
- because it is so common to set the text content, the shortertext
property is preferred -
html
,inner-html
andinnerhtml
are synonyms that will set theinnerHTML
property of theHTMLElement
(important: what's the difference between innerText and innerHTML?) -
class
will either add a value to theclassList
of the element, or using modifier characters can toggle the presence of a class in the list -
table
will bind data to rows and cells of an HTML<table>
element, maintaining integrity with any headers specified on the table. Binding tables is explained in more depth in its own section -
list
will bind a matchingiterable
to a contained list element. Binding lists is explained in more depth in its own section
Additionally to the above list, any bind property can be used to set the corresponding element attribute to the bound value with optional usage of modifier characters.
Common properties include href
, src
, title
, value
, etc.
-
<h1 data-bind:text>Name</h1>
- the bind property istext
, and there is no bind key specified. A value can be passed to thebindValue
function, which will set theh1
's innerText. The value passed must be astring
orStringable
object, or acallable
that returns astring
/Stringable
. -
<input name="user" data-bind:value="username" required />
- the bind property isvalue
, and the bind key is specified asusername
. -
<img src="/blank.png" alt="Profile image" data-bind:src="profileImage" />
- the bind property issrc
, and the bind key is specified asprofileImage
. Note that if we do not bind anything to this element, the default src is already set in the HTML. -
<li class="menu-option" data-bind:class=":selected"><a href="/contact">Contact us</a></li>
- the bind property isclass
and the bind key isselected
, with a boolean bind modifier. Read more about modifier characters.
Bind keys are the optional value of the data-bind attribute on an element that corresponds to the key of a key-value-pair data structure. As an example, <a data-bind:href="url">Click me</a>
has a bind key of url
, and <h1 data-bind:text>Product Name</h1>
has no bind key.
Adding a bind key to a data-bind attribute is optional. When $binder->bindValue($value)
is called, it will bind the provided value to all elements in the Document that have a data-bind attribute without a bind key. It's possible to pass a context element as the second parameter like this: $binder->bindValue($value, $element)
, which can be used to reduce the scope of what elements are bound.
When elements do have a bind key, they can be bound by calling $binder->bindKeyValue($key, $value);
, where $key
is the name of the bind key to use. This technique allows a single HTMLElement to set multiple bind properties, but also enables us to use more advanced DOM Template functionality.
Rather than setting each key and value with individual calls to the bindKeyValue
function, it's possible to pass a key-value-pair data structure to the bindData
function, such as an associative array, or an object with public properties or functions that represent bind keys. This allows you to pass any model of your application directly into the document binder without having to process the data first. Learn more about binding objects.
When binding the document using objects to represent the data, it's possible that objects will have nested properties of other objects. These nested properties can be addressed in the bind key by using the dot character (.
) as a separator. Usage within HTML looks like this: <span data-bind:text="customer.address.country.name">Country name</span>
- learn more about nested bind keys on the Binding objects page.
On any instance of the DocumentBinder
, the following bind functions are available to you:
bindValue($value, [$context])
bindKeyValue($key, $value, [$context])
bindData($kvp, [$context])
bindList($list, [$context])
bindListCallback($list, $callback, [$context])
bindTable($tableData, [$context])
All functions take appropriate arguments for binding the type of data they deal with, along with an optional $context
argument, which allows you to pass an Element
to restrict the scope of the binding within the document.
To keep this documentation easy to read, the following type aliases are used:
-
BindableValue
can be any value that can be cast to astring
- the actual value that will be bound to the Document. You may pass a scalar value such as anint
, or anyStringable
object, and PHP will cast the value to a string prior to binding. If a callable is passed, it will be called once at the time of binding, and thestring
value of what is returned will be used for binding. -
BindableKVP
can be an associativearray
,ArrayAccess
,object
with public parameters, or anobject
with one or moreBind
-Attributed functions. Values of the array, object properties or return types of the object's methods must beBindableValue
. See binding objects for more information on how to use instances of your application's classes to bind to the Document. -
BindableTable
can be aniterator<int, array<int, BindableValue>>
,iterator<int, array<int|string, BindableValue>>
oriterator<array<int, BindableValue>>>
. See binding tables for more information. -
BindableList
can be aniterator<BindableKVP>
. See binding lists for more information.
With all bind functions, the last parameter is an optional Element
called $context
, which defines the scope of where the binding will occur. Passing an Element
object will only bind elements within the tree of that Element. Leave the parameter unset to have the scope set to the whole Document.
DocumentBinder::bindValue(BindableValue $value, ?Element $context = null):void
Calling bindValue
will set the string representation of $value
anywhere within the $context
that there is a data-bind
attribute that has no bind key (no value to the data-bind
attribute).
Example for bindValue
Source HTML:<p data-bind:text>This is a quick example</p>
PHP:
function example(DocumentBinder $binder):void {
$binder->bindValue("This is an updated example");
}
Output HTML:
<p>This is an updated example</p>
DocumentBinder::bindKeyValue(string $key, BindableValue $value, ?Element $context = null):void
Calling bindKeyValue
will set the string representation of $value
anywhere within the $context
that there is a data-bind
attribute that has a bind key matching $key
.
Example for bindKeyValue
Source HTML:
<h1>Hello, <span data-bind:text="name">you</span>!</h1>
PHP:
function example(DocumentBinder $binder):void {
$binder->bindKeyValue("name", "Cody");
}
Output HTML:
<h1>Hello, <span>Cody</span>!</h1>
DocumentBinder::bindData(BindableKVP $data, ?Element $context = null):void
When you have a data stricture that contains multiple key-value-pairs, you can call the bindData
function to set each key-value-pair in one operation.
The $data
parameter can be an associative array
, ArrayAccess
, object
with public parameters, or an object
with one or more Bind
-Attributed functions. Values of the array, object properties or return types of the object's methods must be BindableValue
. See binding objects for more information on how to use instances of your application's classes to bind to the Document.
Example for bindData
Source HTML:
<h1>User profile</h1>
<div>
<h2 data-bind:text="username">Username</h2>
<p>Full name: <span data-bind:text="fullName">Full Name</p>
<p>Bio: <span data-bind:text="bio">Bio goes here</span></p>
</div>
PHP:
function example(DocumentBinder $binder):void {
// In a real application, $data might be supplied from the database
// and could contain model objects rather than associative arrays.
$data = [
"username" => "PhpNut",
"fullName" => "Larry E. Masters",
"bio" => "Christian - Dad - 4x Grandad - Co-Founder of @CakePHP - Developer - Open Source Advocate",
];
$binder->bindData($data);
}
Output HTML:
<h1>User profile</h1>
<div>
<h2>PhpNut</h2>
<p>Full name: <span>Larry E. Masters</p>
<p>Bio: <span>Christian - Dad - 4x Grandad - Co-Founder of @CakePHP - Developer - Open Source Advocate</span></p>
</div>
DocumentBinder::bindList(BindableList $listData, ?Element $context = null):int
An HTML Element can be marked as a list element so that it's repeated for every item in the BindableList
. This is done by adding the data-list
attribute to the element you wish to repeat. By doing this, the original element will be removed from the Document but its original position will be remembered. That way, when a BindableList
is bound, the original list element will be cloned for every item in the list, data from each BindableKVP
will be bound on each new cloned element, and finally each cloned element will be placed back into the Document in the position of the original list element.
When only one list is represented in a given context, there is no need to add a value to the data-list
attribute, but it is possible to bind multiple lists, or even nested lists, using named list elements or providing a context. See binding lists for more information.
Example for bindList
Source HTML:
<h1>Shopping list</h1>
<ul>
<li data-list data-bind:text>Item name</li>
</ul>
PHP:
function example(DocumentBinder $binder):void {
$listData = [
"Eggs",
"Potatoes",
"Butter",
"Plain flour",
];
$binder->bindList($listData);
}
Output HTML:
<h1>Shopping list</h1>
<ul>
<li>Eggs</li>
<li>Potatoes</li>
<li>Butter</li>
<li>Plain flour</li>
</ul>
DocumentBinder::bindListCallback(BindableList $listData, callable $callback, ?Element $context = null):int
The functionality of bindListCallback
is identical to bindList
, apart from you can supply a callable
as the second parameter, which will be called for every iteration of the BindableList
.
The callback will be called with the following parameters:
-
Element $element
the newly-inserted element for the current iteration - data will not have been bound yet -
array $listItem
the current iterable value, converted to an associative array -
int|string $listKey
the current iterable key
The callback must return the value of $listItem
, allowing you to manipulate it in the callback.
Within the callback, you may wish to modify the list element. The provided $element
is a clone of the original list element. It has not had its data bound yet, but it has been attached to the document at the correct location.
Being able to modify the list element and manipulate the value of $listItem
is useful for when data is being provided from a source that is difficult/inefficient to manipulate beforehand.
Example 1 for bindListCallback
Source HTML:
<h1>Shopping list</h1>
<ul>
<li data-list data-bind:text>Item</li>
</ul>
PHP:
function example(DocumentBinder $binder):void {
$listData = [
"Eggs",
"Potatoes",
"Butter",
"Plain flour",
];
$binder->bindListCallback($listData, function(Element $element, $listItem, $listKey) {
$element->classList->add("item-$listKey");
return "$listItem (item $listKey)";
});
}
Output HTML:
<ul>
<li class="item-0">Eggs (item 0)</li>
<li class="item-1">Potatoes (item 1)</li>
<li class="item-2">Butter (item 2)</li>
<li class="item-3">Plain flour (item 3)</li>
</ul>
Example 2 for bindListCallback - two nested lists
Source HTML:
<h1>Menu</h1>
<ul>
<li data-list>
<h2 data-bind:text="title">Menu item title</h2>
<p>Ingredients:</p>
<ul>
<li data-list data-bind:text>Ingredient goes here</li>
</ul>
</li>
</ul>
PHP:
function example(DocumentBinder $binder):void {
$listData = [
[
"title" => "Roast king oyster mushroom",
"ingredients" => ["hazelnut", "summer truffle", "black garlic"],
],
[
"title" => "Cornish skate wing",
"ingredients" => ["borlotti cassoulet", "lilliput caper", "baby gem", "orange oil"],
],
[
"title" => "Aged Derbyshire beef",
"ingredients" => ["turnip", "pickled mustard", "bone marrow mash", "rainbow chard"],
],
];
$binder->bindListCallback($listData, function(Element $element, $listItem, $listKey) use($binder) {
$binder->bindKeyValue("title", $listItem["title"]);
$binder->bindList($listItem["ingredients"], $element);
});
}
Output HTML:
<!DOCTYPE html>
<html>
<body>
<h1>Menu</h1>
<ul id="list-parent-62fe445401332">
<li>
<h2>Roast king oyster mushroom</h2>
<p>Ingredients:</p>
<ul id="list-parent-62fe44540144e">
<li>hazelnut</li>
<li>summer truffle</li>
<li>black garlic</li>
</ul>
</li>
<li>
<h2>Cornish skate wing</h2>
<p>Ingredients:</p>
<ul id="list-parent-62fe44540144e">
<li>borlotti cassoulet</li>
<li>lilliput caper</li>
<li>baby gem</li>
<li>orange oil</li>
</ul>
</li>
<li>
<h2>Aged Derbyshire beef</h2>
<p>Ingredients:</p>
<ul id="list-parent-62fe44540144e">
<li>turnip</li>
<li>pickled mustard</li>
<li>bone marrow mash</li>
<li>rainbow chard</li>
</ul>
</li>
</ul>
</body>
</html>
DocumentBinder::bindTable(BindableTable $tableData, ?Element $context = null):void
Outputting a data structure to an HTML Table could easily be achieved by adding the data-list
attribute to a <tr>
element, then using bindList
an described in the previous example. However, because HTML tables can define their data structure by defining a <thead>
, the bindTable
function allows mapping different data structures to a pre-defined output.
The content of BindableTable $tableData
can be:
- An
iterable
withinteger
keys, whose value is an iterator ofBindableValue
s (where the first value represents the headers) - this is the data structure provided byfgetcsv
- An
iterable
withstring
keys that match the table headers, whose value is an array ofBindableValue
s - An
iterable
withinteger
keys, where the first value is an iterator ofBindableValue
s representing the headers, and the second value is aniterator
withstring
keys where the key represents the first field of the row and the value is aniterator
ofBindableValue
s representing subsequent field values
The different types of data structure BindableTable
can represent, and more information on usage, see the binding tables section.
Example for bindTable
Source HTML:
<table>
<thead>
<tr>
<th>Day</th>
<th>Weather</th>
</tr>
</thead>
</table>
PHP:
function example(DocumentBinder $binder):void {
$tableData = [
"Day" => ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
"Weather" => ["Rain", "Cloud", "Cloud", "Sun", "Sun", "Cloud", "Cloud"],
];
$binder->bindTable($tableData);
}
Output HTML:
<table>
<thead>
<tr>
<th>Day</th>
<th>Weather</th>
</tr>
</thead>
<tbody>
<tr>
<td>Mon</td>
<td>Rain</td>
</tr>
<tr>
<td>Tue</td>
<td>Cloud</td>
</tr>
<tr>
<td>Wed</td>
<td>Cloud</td>
</tr>
<tr>
<td>Thu</td>
<td>Sun</td>
</tr>
<tr>
<td>Fri</td>
<td>Sun</td>
</tr>
<tr>
<td>Sat</td>
<td>Cloud</td>
</tr>
<tr>
<td>Sun</td>
<td>Cloud</td>
</tr>
</tbody>
</table>
Binding a null
has different behaviour to binding an empty string ""
. Binding an empty string will set the bind property to an empty string, but binding null
will leave the HTML unaffected. With this knowledge, you can provide default values within the HTML and further reduce the logic required in PHP to manipulate the data to the desired shape.
Once an element's property has been bound once, its value will not change again with subsequent binds unless the element has a data-rebind
attribute. This feature allows for a style of programming where specific areas of the document can be bound first, before applying a document-wide bind.
For example, on an "Order summary" page of an e-commerce site, a list of orders can be bound to a specific element of the page. Each Order
might have common bind keys such as id
, name
, etc. After binding this list to a specific element, a User
object can be bound to the entire document, and any unbound id
, name
keys on the page will take that of the User. This is just an example of one style of programming that can help save a lot of time implementing common page layouts.
Next up, learn how to use bind key modifier characters.
PHP.Gt/DomTemplate is a separately maintained component of PHP.Gt/WebEngine.
- Bind data to HTML elements with
data-bind
attributes - Bind key modifiers
- Inject data into HTML with
{{curly braces}}
- Bind lists of data with
data-list
attributes - Bind nested lists with
data-bind:list
- Automatically remove unbound elements
- Bind tabular data into HTML tables
- Using objects to represent bindable data