Data views and controllers
Data views and controllers connect the document with the data in a given data model. Data views are used to present a data variable in the document by different means. On the other hand, data controllers are used to respond to changes in the document, typically as a result of user input. When a controller is triggered, it sets a data variable in its data model.
Data views and controllers are declared in the document by the element attribute:
data-[type]-[modifier]="[value]"
The modifier may or may not be required depending on the data view/controller. Some data bindings attach both a view and controller from the same attribute, enabling two-way bindings.
The following table lists all built-in data views and controllers in RmlUi, along with their declaration.
Name | Type | Attribute | Value | Notes |
---|---|---|---|---|
Attribute | View | data-attr-[attribute_name] | [data_expression] | |
Attribute-if | View | data-attrif-[attribute_name] | [data_expression] | |
Class | View | data-class-[class_name] | [data_expression] | |
Style | View | data-style-[property_name] | [data_expression] | |
If | View | data-if | [data_expression] | |
Visible | View | data-visible | [data_expression] | |
For | View | data-for | [iterator_name], [index_name] : [data_address] | [1] |
Rml | View | data-rml | [data_expression] | |
Text | View | N/A | N/A | [2] |
Alias | View | data-alias-[alias_name] | [data_address] | |
Value | Two-way | data-value | [data_address] | [3] |
Checked | Two-way | data-checked | [data_address] | [3] |
Event | Controller | data-event-[event_type] | [assignment_expression] |
[1] iterator_name
and index_name
are optional. Defaults to it
and it_index
, respectively.
[2] The text view is automatically added whenever double curly brackets {{ }} are encountered in the element’s text.
[3] These attributes enable two-way bindings, and will attach both a view and controller to the element.
When data views are updated, their data expressions are evaluated and applied to the document using any necessary type conversion, which is specified by the kind of the data view. Type conversion is done using RmlUi’s built-in TypeConverter
utilities. One aspect of this conversion is that booleans are converted to strings "0"
or "1"
. Consider an element
<div data-attr-foo="user_data"></div>
where the value user_data
is bound to a C++ variable bool user_data = true
. The element’s attribute will be set to foo="1"
. Any associated RCSS attribute selector should use the same representation of the value, i.e. div[foo=1]
.
Attribute
data-attr-[attribute_name]="[data_expression]"
Sets the element’s attribute [attribute_name]
to the evaluated expression.
<img data-attr-sprite="item.icon"/>
Attribute-if
data-attrif-[attribute_name]="[data_expression]"
Sets the element’s attribute [attribute_name]
when the expression evaluates to true
, otherwise removes the given attribute from the element.
<input type="checkbox" name="meals" value="pizza" data-attrif-disabled="rating > 70"/>
Useful for element behavior which depends on whether or not the attribute is present, such as disabled
. When set, the value of the attribute is an empty string.
Class
data-class-[class_name]="[data_expression]"
Enables the class [class_name]
on the element if the expression evaluates to true
, otherwise it disables the class.
<h1 data-class-red="score < 30">Score</h1>
Style
data-style-[property_name]="[data_expression]"
Sets the property [property_name]
of the element’s style to the evaluated expression.
<img sprite="invader" data-style-image-color="invader.color"/>
If
data-if="[data_expression]"
Sets the display
property of the element to none
if the expression evaluates to false
, otherwise it removes the display
property from the element’s inline style.
<div data-if="rating > 50">
Thanks for the <span data-if="rating >= 80">awesome</span> rating!
</div>
Note. The style sheet rules which applies to the element should ensure that the element’s display
property evaluates to something other than none
. Otherwise, the element will always be hidden.
Visible
data-visible="[data_expression]"
Sets the visibility
property of the element to hidden
if the expression evaluates to false
, otherwise it removes the visibility
property from the element’s inline style.
<div data-visible="collected_stars > 0">
<img sprite="star"/>
</div>
As opposed to the data-if
view, the data-visible
view ensures that the element retains it’s size regardless of visibility.
Note. The style sheet rules which applies to the element should ensure that the element’s visibility
property evaluates to visible
, which is the default value. Otherwise, the element will always be hidden.
For
data-for="[iterator_name], [index_name] : [data_address]"
Repeats the element and its children n times for each item in the data variable designated by the data_address
. The variable must be a data array type.
<div data-for="invader : invaders">
<h1>{{ invader.name }}</h1>
<p>Invader {{it_index + 1}} of {{ invaders.size }}.</p>
<img data-attr-sprite="invader.sprite" data-style-image-color="invader.color"/>
<p>Scores: <span data-for="invader.scores"> {{it}} </span></p>
</div>
An iterator can be used to retrieve values from the current item in the data array.
The data-for
attribute can use any of the following values, enabling the user to override the default iterator and index names if desired. Note that the index is zero-based.
Attribute value | Iterator name | Index name |
---|---|---|
[data_address] | it |
it_index |
[iterator_name] : [data_address] | [iterator_name] | it_index |
[iterator_name], [index_name] : [data_address] | [iterator_name] | [index_name] |
The data-for
loop is expanded by replicating the element with its attributes and its inner RML, for each entry in the array. Eg.
<p data-for="subject, i : subjects" data-class-selected="i == selected_subject">{{i + ': ' + subject}}</p>
with three entries in subjects
is turned into
<p data-class-selected="i == selected_subject">{{i + ': ' + subject}}</p>
<p data-class-selected="i == selected_subject">{{i + ': ' + subject}}</p>
<p data-class-selected="i == selected_subject">{{i + ': ' + subject}}</p>
<p style="display: none;"/>
where i
and subject
become aliases to the array index and entry, respectively. Additionally, an element is added after all the entries so that the location of the for loop within the document tree is well defined even when there are no entries. This will become hidden by the display: none
inline style added by the data view.
Note. For performance reasons the names of global data variables shadow iterator names. Thus, do not use an iterator name which is used for a data binding.
Implementation note. Internally, the XML parser uses a special parsing rule whenever the data-for
attribute is encountered, providing all the children of the current element as raw RML text to the data view, which is later used for creation of each item in the data array.
Rml
data-rml="[data_expression]"
Sets the element’s inner RML to the evaluated expression.
<div data-rml="incoming_invaders ? '<em>Send help!</em>' : 'Clear skies.'">
</div>
Text
N/A
Evaluates any data expression inside double curly brackets {{ }} encountered in the element’s text.
<span class="position"> x: {{ position.x }}, y: {{ position.y }}</span>
<span data-for="i : indices"> {{ i * 2 + (i > 10 ? ' wow!' | to_upper : '') }}</span>
This data view is automatically added whenever double curly brackets are encountered in the text and should not be added as an attribute.
Alias
data-alias-[alias_name]="[data_address]"
Creates a new alias variable at the given scope, allowing the stated data address to be referred to by its alias name.
This allows templates to be used as reusable components within data models. By wrapping the inline template in an element that defines variable name aliases, the template can refer to any outside variable by a fixed name.
To illustrate, consider the following template.
<template name="data-title">
<head></head>
<body>
<div class="icon" data-attr-icon="icon"></div>
<h1 class="title">{{ title }}</h1>
</body>
</template>
This template can then be used with different variables as follows:
<div data-alias-title="t0" data-alias-icon="i0">
<template src="data-title"/>
</div>
<div data-alias-title="t1" data-alias-icon="i1">
<template src="data-title"/>
</div>
Value
data-value="[data_address]"
Synchronizes the element’s value
attribute to the value of the data variable located at data_address
. This variable must be a scalar type. This is generally useful for input
elements.
<input type="range" min="0" max="100" step="1" data-value="rating"/>
A new value is assigned to the specified data variable whenever a change
event occurs on the current element. The element’s value
attribute is updated whenever the data variable changes on the client side.
Note. Data expressions and assignment expressions are not supported for this attribute. Instead, use the data-attr-value
view and data-event-change
controller for more flexibility.
Checked
data-checked="[data_address]"
Binds a checkbox or radio button’s checked
state to the variable located at data_address
. This variable must be a scalar type. Typically combined with <input type="checkbox"/>
and <input type="radio"/>
elements.
<input type="radio" name="animal" value="dog" data-checked="animal"/> Dog
<input type="radio" name="animal" value="cat" data-checked="animal"/> Cat
<input type="checkbox" name="meals" value="pasta" data-checked="pasta"/> Pasta
For checkboxes, the underlying data type should be a bool
, where true
means checked and false
means unchecked. For radio buttons, the underlying type should be an Rml::String
type where its value corresponds to the value
attribute of the currently selected radio button.
A new value is assigned to the specified data variable whenever a change
event occurs on the current element. The element’s checked
attribute is added or removed whenever the data variable changes on the client side.
Note. Data expressions and assignment expressions are not supported for this attribute. Instead, use the data-attrif-checked
view and data-event-change
controller for more flexibility.
Event
data-event-[event_type]="[assignment_expression]"
The event controller is triggered whenever the [event_type]
event occurs on the current element. All event types in RmlUi are supported. Upon triggering, the associated assignment expression is evaluated.
An assignment expression is specified as one of the following two statements.
(1) [data_address] = [data_expression]
(2) [event_callback_name]([data_expression], [data_expression], ...)
Furthermore, a single assignment expression can take multiple such statements by semicolon-separating them.
In (1), the data variable associated with the address on the left hand side is assigned the evaluated expression on the right hand side. Only scalar types can be assigned to.
In (2), the given event callback is called in C++, with the triggering event itself, a handle to the current data model, and the list of parameters inside the parenthesis.
The special variable ev
can be used inside the expressions to retrieve values from the triggering event.
<div class="mouse_detector"
data-event-mousemove="mouse_detector = 'x: ' + ev.mouse_x + '<br/>y: ' + ev.mouse_y"
data-event-click="add_mouse_pos(); hello_world = 'Hello click!'"
data-rml="mouse_detector">
</div>
<h1>{{hello_world}}</h1>
<div data-for="positions">{{it}}</div>
The referenced add_mouse_pos
event callback is triggered when the element is clicked, which can be implemented in C++ as follows.
using namespace Rml;
std::vector<Vector2f> positions;
void AddMousePos(DataModelHandle model_handle, Event& ev, const VariantList& arguments)
{
positions.emplace_back(ev.GetParameter("mouse_x", 0.f), ev.GetParameter("mouse_y", 0.f));
model_handle.DirtyVariable("positions");
}