Skip to content

Page and Portal State

A portal page works with a few kinds of state. Knowing which is which tells you where a value lives and how long it lasts.

Path What it holds Lifetime
$params The page's parameter values — its own and those declared on the layouts it uses Own parameters: while you are on the page. Layout parameters: for the whole visit
$data Results of the page's data sources — its own and the layouts' Own sources: reloaded every time you open the page. Layout sources: loaded once, reused across pages
$vars Anything you put there — a row a table selected, a value one widget wants another widget to see While you are on the page
$portal.state Anything you put there, when it must survive moving to another page For the whole visit, across every page
$user The person signed in and viewing the portal — read-only For the whole visit

Reloading the browser starts a fresh visit: $vars and $portal.state are empty again and every parameter returns to its default. That is expected.

Connecting two widgets on a page — $vars

$vars is the simplest way to make one widget react to another. One widget writes a value, the others read it. There is nothing to declare: write to any name you like.

A table publishes the row you click by pointing its selection at a $vars path:

$vars.selectedRow

Any other widget on the page then reads it, in text:

Selected customer: {$vars.selectedRow.name}

…or in a Visible Expression, to show a details panel only once something is picked:

{$vars.selectedRow} != null

…or as a parameter mapping for a data source, so the details load for whatever is selected.

Anything works the same way: bind an input to $vars.searchTerm and filter a list on it, write $vars.activeTab from a button and switch panels on it.

$vars belongs to the page you are on. Open another page and it starts empty again — that is what makes it safe to use for scratch values. When a value must survive moving to another page, use $portal.state instead.

The person viewing the portal — $user

$user is the signed-in user. It is always there and you never declare it, on every page and every layout:

Path What it holds
{$user.displayName} The name to greet them by
{$user.email} Their email address
{$user.firstName}, {$user.lastName} The two halves of the name
{$user.id} Their numeric user id
{$user.language} Their language code, e.g. en
{$user.workspaceId}, {$user.workspaceName} The workspace the portal belongs to

Use it in text:

Welcome back, {$user.displayName}

…in a Visible Expression, to show a panel to one person only:

{$user.email} == '[email protected]'

…or as a parameter mapping, so a data source loads the signed-in person's own rows:

{$user.email}

$user is read-only. Binding an input to $user.email does nothing — the value comes from who is signed in, and a write to it is ignored.

$user is not a security boundary

Expressions are evaluated in the browser, so a parameter derived from $user is sent from the browser like any other. Somebody determined can change it before it reaches the server. Use $user to show the right thing to the right person and to save them picking their own name from a filter — never as the only thing standing between a user and data they should not see. Where the data itself must be restricted, restrict it in the query, the database, or the API behind the data source.

Shared layout parameters

A parameter declared on a layout is shared by every page that uses that layout. Pick a year in a filter on the layout, move to another page, and the same year is selected. A page can declare its own parameter with the same name; on that page the page's value wins, and the layout's value is untouched for the other pages.

Layout data sources are loaded once

A data source declared on a layout — for example the current user — is fetched when it is first needed and then reused on every page that shares the layout. It is fetched again only when something it depends on changes (a parameter it uses, or another data source it reads).

Values that outlive the page — $portal.state

$portal.state works exactly like $vars — bind to any path under it, no declaration needed — but its values are kept for the whole visit and are visible on every page. Use it for a region picked on one page and used on the next, and $vars for everything that only concerns the page you are on.

$portal.scopes is where the runtime keeps the values behind $params, $data and $vars. You can read it (the Data Explorer shows it), but writes to it are ignored — change a parameter through $params instead.

Inspecting state

In the page editor, Data Explorer shows $params, $data, $vars, $user and $portal as the current page sees them, including which layout each shared value comes from.