virtual
A computed expression. Re-evaluates whenever its dependencies change. Use this for derived globals (full name from first plus last, formatted dates, totals).
Xuda Developer Studio
Globals are how you keep state that should outlive a single page: the current user, the active locale,
a feature flag, the current theme, the cart, the WebSocket connection. Internally they are just
progFields on a program with menuType: 'globals'. The studio gives that
program its own panel; the runtime exposes its fields under the @ scope of every other
program.
Open the project document and the globals object lives at project.globals. It looks like
any other program: it has an _id, a properties bag, and a progFields
array. The only differences are the menuType: 'globals' marker and the empty (or absent)
progUi, since globals never render anything by themselves.
{
_id: 'globals',
ViewEventObj: {},
properties: { menuType: 'globals' },
progFields: [
{
data: { field_id: 'currentUser', type: 'virtual' },
props: { propExpressions: { fieldValue: '({ id: null, name: \"\" })' } }
},
{
data: { field_id: 'theme', type: 'virtual' },
props: { propExpressions: { fieldValue: '\"dark\"' } }
},
{
data: { field_id: 'flag_betaUI', type: 'virtual' },
props: { propExpressions: { fieldValue: 'false' } }
}
]
}
Globals are not limited to plain literals. The data.type on a field tells the runtime how
to compute its value. The four common types apply to every program, but they show up most clearly here.
A computed expression. Re-evaluates whenever its dependencies change. Use this for derived globals (full name from first plus last, formatted dates, totals).
Bound to a row from a table or a subscription. The runtime keeps the field in sync with the underlying record. Use this for the current user document or the current cart.
Returned by a library plugin call. Useful when the value comes from an external service (geo location, push token, billing customer).
A fixed initial value. Mutable at runtime via xu-click updates. The simplest kind: a string, a number, an object.
@ scope. No import. No prop drilling.
Every xu-exp:* expression in any program runs against a merged scope: program fields,
loop variables, parent stores, and globals. To reference the global currentUser, write
@currentUser. The same prefix that reads a local field reads a global, because at evaluation
time both flatten into the same lookup.
<header xu-exp:xu-content="'Welcome, ' + @currentUser.name"></header> <!-- Conditional rendering by feature flag --> <section xu-exp:xu-render="@flag_betaUI"> Beta dashboard goes here. </section> <!-- Locale-aware label --> <p xu-exp:xu-content="@user_locale === 'he-IL' ? 'שלום' : 'Hello'">Hello</p>
xu-click grammar that updates local store updates a global.
Updates targeting a global field walk up the scope chain until they find the globals store. The
expression syntax is identical to a local update, so promoting a local store to a global
is a one-line change in the field definition, not a refactor of every reader.
<button xu-click="{'@theme':'light'}">Light theme</button>
<button xu-click="{'@flag_betaUI': !@flag_betaUI}">Toggle beta UI</button>
<!-- Workflow form (xu-on:click) for a multi-step update -->
<button xu-on:click='[{"workflow":[
{"data":{"action":"update","name":{"value":"@currentUser: null"}}},
{"data":{"action":"navigate","name":{"value":"/login"}}}
]}]'>Sign out</button>
The most common mistake is putting everything in globals. Globals are global. Two unrelated screens that happen to use a field with the same name will fight each other. Reach for a global only when the value truly belongs to the whole app.
progEvents can dispatch a custom event on
document.body, and any other program can listen with xu-on:<event_name>.
That keeps the relationship explicit instead of implicit through shared global state.
In the studio sidebar, the Globals entry opens the same field editor used for program fields. You add a
row, pick a type, give it an id, and supply an expression or a binding. The runtime picks
up the change instantly in the live preview because the project document has updated.
When you add a global in the studio, the row that lands in the project document is exactly:
{
data: {
field_id: 'currentUser',
type: 'datasource',
serverField: false
},
props: {
propExpressions: {
// For type: 'datasource' the binding goes here:
fieldDataSource: 'tbl_users',
fieldQuery: '({ _id: @authToken.user_id })',
// For type: 'virtual' the expression goes here:
// fieldValue: 'expression-string'
// For type: 'literal' the value goes in props.fieldValue directly.
}
}
}