Skip to main content

Regions

A region is an empty, framework-owned container whose body is drawn by a named JavaScript function (a populator) instead of server-emitted markup. The framework owns the container, its identity, its lifecycle and its cancellation; the populator owns everything inside it.

There are exactly three region types, the RegionDef constants:

ConstantWhere it lives
TYPE_ROW_DETAIL (row-detail)Inside a table row's expandable detail panel.
TYPE_CARD_BODY (card-body)In the body of a card.
TYPE_TAB_BODY (tab-body)In a tab's body; lazy by default.

Declaring a region​

RegionDef (in org.apache.juneau.console.views) is the server-side descriptor. RegionDef.CONTRACT_VERSION is "1" and is independent of the view, action, bulk and slot versions.

FieldMeaning
idUnique within the host.
populateName of the client populator, or null for the built-in default. A name, not a function, so a server-side allowlist can constrain it.
typeOne of the three types above. Usually set by the enclosing host.
dataUrlSame-origin path the declarative default fetches from. null means no declared fetch.
paramsRequest parameters appended to dataUrl. Values may not be nested maps and keys may not collide with dataUrl's own query string.
rendererThe JuneauViews.helpers function the default populate paints with, for example field-grid.
lazyDefer the first populate until visible. Defaults to false for row-detail and card-body, true for tab-body (effectiveLazy()).
refreshMsAuto-refresh interval. A value below RegionDef.MIN_REFRESH_MS is clamped up to it (5000 ms on the client).
fieldsThe field catalog for card and tab regions (RegionDef.Field: label, render, href, format, span, actions). A row-detail region must not declare one.
titleFieldsTitle keys the chrome's title painter may read. Blank entries are rejected.
allowedPopulatorsExtra populator names allowed in addition to the built-ins.
configStructured configuration owned by the populator: nested maps and lists are allowed. It is never appended to dataUrl or sent to the server, and is emitted only when non-empty, so a region without it has the same sidecar as before. The populator reads it as ctx.declared.config.

RegionDef.validate() is fail-closed. It rejects a blank id, a disallowed or blank populator, an unknown type, a cross-origin dataUrl, a nested-map param, a colliding param key, a config that isn't JSON-serializable, a blank title field, and a field catalog on a row-detail region or an empty one where field-grid requires it. Field renderers must be built-in ids; see Security Model.

Emitting a region​

RegionTable.of(RegionDef) produces the empty container <div class='juneau-region'> carrying identity, type and the handshake stamp. RegionTable.sidecar(hostId, regions) emits the per-host JSON sidecar; a row-detail host whose subtree is cloned per row uses RegionTable.detailSidecar(regions), which has no id to avoid collisions across open panels. In a row-detail region pass no CSRF token, since the enclosing table already stamped one.

RegionTable.of(region, csrfToken) stamps the token into the response, so a host that uses it must call RequestBoundaryFilter.markTokenBearing(response) (Cache-Control: private, no-store); otherwise a shared cache can serve one session's token to another. See REST Security.

Writing a populator​

JuneauViews.regions.register("pet-history", function populate(ctx, container) {
// container is empty, in the document, and yours.
// Return nothing, a cleanup function, a Promise, or a Promise of a cleanup function.
const p = document.createElement("p");
p.textContent = "Loaded " + ctx.declared.dataUrl;
container.append(p);
return function cleanup() { /* unsubscribe */ };
});

The ctx carries an abort signal that fires on teardown, a scoped emit and on for the page-wide message bus, and the declared values from the RegionDef (ctx.declared, with config present only when the region declares one). A region sees only its own ctx, so the framework can sweep all of a region's subscriptions when it tears down. A page with no regions pays nothing; the bus is created at first enrolment.

The runtime requires AbortController, and refuses to populate when it is missing rather than degrading without a signal.

A declared region may name a populator only if the name is a built-in or is listed in allowedPopulators.

In an author-written page slot, JuneauViews.regions.mount(hookup) takes a map of element id to populator name and enrols one region per entry:

JuneauViews.regions.mount({ "history-slot": "pet-history" });

It returns a thenable of the region handles. A missing element id, a blank name or an unregistered populator fails loudly and enrols nothing.

The default populator​

A region with no populate uses the built-in default, which fetches dataUrl (with params) and paints the result through the named renderer, typically field-grid with the declared fields. This is the declarative path: no JavaScript required.

The terminal-view and terminal populators​

terminal-view and terminal are built-in populators that are not allowed by default. Each draws a live pane that runs its own polling loop, so it ignores the region's dataUrl and refreshMs. TerminalViewDef.toRegion() and TerminalDef.toRegion() opt in for you; a RegionDef you build yourself must call allowPopulators("terminal-view") or allowPopulators("terminal"). See Terminal View Region and Terminal Region.

The job-log populator​

job-log runs a job for the row whose detail panel holds the region: a small form, a key fetched for that row, and the job's streamed log. It comes from the console's joblog asset pack (juneau-joblog.js), not from the views runtime, so it is a console-page feature. Its config is the job-log card's body plus submit.keyUrl, checked by JobLogChecks. JobLogRegion.toRegion() builds the region with type row-detail, populate job-log, the populator allowed, and the config; a datatables card declares the same thing as detail.region with populate: 'job-log'. See Job-Log Card and Row Jobs.

Polling while a panel runs a job​

A table that polls redraws its rows on every tick, and a redraw closes any open detail panel. So while an expanded panel hosts a row job that is running (or still being submitted), the timer's reload is held for that table and the staleness pill shows Paused. This applies to every view, whether or not it sets pausePollingWhileEditing. Only the timer is held: a user reload, a sort, a filter or a page change still redraws, and re-expanding the row re-attaches the region to the running job with its log replayed from the start. A collapsed row's job and a job on a row that isn't drawn don't hold the poll. Neither does a row action's job, even with its row's panel open: it streams into the row, not the panel. When the job ends or the panel collapses, the next tick redraws as usual.