Views & Slots
View declaration
The view { } block declares the component’s visual output as a tree of child component instances:
component App {
view {
ui.Column {
Header {}
Content {}
Footer {}
}
}
}The compiler generates a $init_view function that builds the child tree at instantiation.
Prop binding
Inside a component instance, bind props with name: expr; syntax:
view {
widgets.Label { text: "Hello"; fg: #333333; size: 24; }
widgets.Button { label: "Click"; bg: #4488CC; on_click: handle; }
}Event handlers are bound the same way — pass a function name.
A value that reads the component’s vars, states or props
(text: "clicks {count}", size: n % 3, title: name) is evaluated again
whenever one of those changes, and the child repaints if its prop got a
different value. A value that reads a module variable or calls a function
is evaluated when the instance is built and again only when the program
calls ui.invalidate(comp):
keep a view’s state in the component and change it through its methods,
and use invalidate for state changed outside the tree.
Row names the module’s own component; ui.Row, tk.Row the one the
module imported as ui or tk declares, even when several declare a
Row.
Conditional rendering
component Profile {
prop logged_in: bool;
view {
ui.Column {
if (logged_in) {
UserPanel {}
} else {
LoginForm {}
}
}
}
}Conditions must be bool (no truthy values) and require parentheses.
List rendering
Use for (item in collection) to render a component for each element in a slice:
component TodoList {
prop items: []TodoData;
view {
ui.Column {
for (item in items) {
TodoItem { key: item.id; text: item.text; done: item.done; }
}
}
}
}Whenever the list updates, each item is matched to the instance it had last
time: matched instances are reused with their props refreshed and keep their
var state, new items get new instances, and instances whose items are gone
are unmounted.
key: decides how items are matched. With it, an instance follows its item
when the list is reordered, filtered or grown. Without it, instances are
matched by position — the third instance always shows the third item — so
var state stays at a position rather than with an item. The compiler warns
about a loop without key: whose component has var state.
- A key is an
i64,u64,stringor enum value — usually the item’s id:key: item.id;, orkey: g_items[i].id;in a range loop. - Keys must be unique within the loop. A repeated key is a runtime trap
(
trap: duplicate key in view 'for' loop at file:line:col), and a key that does not depend on the loop variable is a compile warning. key:goes only on the component directly inside thefor, and no component can declare a prop namedkey.
Dotted component names
Components from imported modules use dotted names:
view {
ui.Column {
widgets.Button { label: "OK"; }
widgets.Label { text: status; }
}
}Slots
Slots let a component accept child content from its parent:
component Card {
prop title: string = "";
slot content;
slot footer;
view {
ui.Column {
widgets.Label { text: title; size: 18; }
content;
footer;
}
}
}A bare identifier followed by ; in a view tree is a slot reference — it renders whatever the parent provides.
Filling slots (parent side)
The parent fills slots using slot name { children }; syntax:
Card {
title: "Settings";
slot content {
SettingsForm {}
widgets.Label { text: "Options:"; }
};
slot footer {
widgets.Button { label: "Save"; on_click: save; }
};
}slot is a keyword only here and in a component’s slot name;
declarations. Elsewhere it is an ordinary name: let slot = 1; or a struct
field called slot are fine.
View tree control flow summary
| Construct | Syntax |
|---|---|
| Instance | Name { props; children; } |
| Dotted instance | module.Name { props; } |
| Conditional | if (cond) { ... } else { ... } |
| Loop | for (item in expr) { ... } |
| Slot reference | slot_name; |
Last modified: