Key Concepts
Templates

Templates

A component's template describes that component's content and hierarchy.

Example template:

<Group>
    <Rectangle />
</Group>

Each component declares a template in an XML-like syntax, which describes how its UI should be displayed. A component's template is made up of other components.

Control flow

Templates are not just static -- they allow three kinds of control-flow, affecting the tree structure of the template based on certain data conditions.

if

if allows turning on or off a subtree of a template dynamically, based on a boolean condition. For example:

use pax_lang::api::*;
use crate::{DetailsView, SummaryView};
 
#[pax]
#[main]
#[inlined(
    <Group>
        if self.should_show_details {
            <DetailsView />
        } else {
            <SummaryView />
        }
    </Group>
)]
pub struct IfExample {
    pub should_show_details: Property<bool>
}

Internally, Pax handles evaluation of the if condition as an Expression, via the primitive Conditional.

for

for allows repeating of template elements dynamically based on data.

For example:

use pax_lang::api::*;
use pax_std::layout::{Stacker, StackerDirection};
use crate::DeviceRecord;
 
 
#[pax]
#[main]
#[inlined(
    <Stacker>
        for device_record in self.connected_devices {
            <Stacker direction=StackerDirection::Vertical cells=3>
                <Text text={device_record.id}></Text>
                <Text text={device_record.name}></Text>
                <Text text={device_record.load_capacity}></Text>
            </Stacker>
        }
    </Stacker>
)]
pub struct ForExample {
    pub connected_devices: Property<Vec<DeviceRecord>>
}

Internally, Pax handles the for range declaration as an Expression, via the primitive Repeat.

slot

slot allows a component to defer its content at a certain place in its template. Specifically, it defers its content to the component's instantiator.

For a practical example, consider <Stacker />. When you use a Stacker, you pass children into it, like:

//src/slot-example.rs
#[pax]
#[main]
#[inlined(
    <Stacker>
        <Rectangle id=a />
        <Rectangle id=b />
        <Rectangle id=c />
    </Stacker>
)]
pub struct SlotExample {}

When Stacker renders, like any component, it will render the elements declared in its template, from /pax-std/.../stacker.rs. However, the above rectangles are NOT in Stacker's template; they are declared in SlotExample's template in ./slot-example.rs.

How do we "teleport" these Rectangles from SlotExample's template into Stacker? The answer is slot.

If you pop open the source code for Stacker, you will find that it uses the slot keyword in its template, along with an index specifying "which indexed child should go in this slot."

If we were to write a new simplified Stacker that only accepts three children, its template might look like:

<Frame id=cell_0>
    slot(0) //the 1st child to an instance of this component will get mounted here
</Frame>
<Frame id=cell_1>
    slot(1) //the 2nd child to an instance of this component will get mounted here
</Frame>
<Frame id=cell_2>
    slot(2) //the 3rd child to an instance of this component will get mounted here
</Frame>

Because slot, like if and repeat, is evaluated as an expression, you can also pass symbol values (expressions) into slots arguments. This is how the real Stacker accepts i dynamic children via slot, with a template like:

for (elem, i) in self.computed_layout_spec {
    <Frame x={elem.x_px} y={elem.y_px} width={elem.width_px} height={elem.height_px}>
        slot(i)
    </Frame>
}