Router View Hosts Guide
This guide explains how router-view hosts work in Zova within the Cabloy monorepo.
Use this page after Page Route Guide, A-Router Guide, and Zova Router Under the Hood when your next question is no longer “how is the route registered?” but “which routed host actually owns the page instance, keep-alive behavior, and workspace model?”.
Read this page together with:
- Page Route Guide
- Zova Router Under the Hood
- Router Tabs Introduction
- Router Tabs vs Stack
- Router Stack Guide
- Router Tabs Mechanism
- Page Meta Guide
- Router Tabs Layout Integration
- Zova Source Reading Map
If you reached this page from the routing branch of Zova Source Reading Map, this is the next step after route registration and controller route-state injection.
TIP
Router ecosystem docs path
- Page Route Guide — learn the public route-record and layout surface
- A-Router Guide — learn what the
a-routerpackage owns in the runtime - Zova Router Under the Hood — learn how the core router runtime cooperates
- Router View Hosts Guide — learn how routed pages are actually hosted
- Router Tabs Overview — learn the business/workbench meaning of router tabs
- Router Tabs Mechanism — learn the shared tabs model in code
- Router Tabs Layout Integration — learn how the current Basic layouts turn the shared model into a visible shell
- Zova Source Reading Map — learn which files to read next
You are here: step 4. Previous pages: Page Route Guide, A-Router Guide, and Zova Router Under the Hood. Next recommended pages: Router Tabs Overview, Router Tabs Mechanism, Page Meta Guide, and Router Tabs Layout Integration.
Why this page exists
After contributors understand route records, guards, aliases, and typed params/query, the next practical question is usually about the routed host itself.
Typical follow-up questions are:
- why does one routed page behave like a plain shell page while another behaves like a workbench tab?
- where does keep-alive inclusion come from?
- where do
tabKey,componentKey, and page-level task state actually attach? - what is the difference among
routerViewEmpty,routerViewTabs, androuterViewStack?
This page answers those questions.
The shortest accurate mental model
The shortest accurate model is:
a-routerresolves the target route- the routed page enters a router-view host, not only a bare Vue Router outlet
- the host decides whether the route is treated as plain output, a workbench tab, or a stack item
- the host computes routed identity such as
tabKey,componentKey, and keep-alive participation - the shared router bean calls host callbacks on forward/back navigation and page-meta updates
- the active layout chooses which host component to render
That is why routed-page behavior in Zova is not only a route-record concern. It is also a host-selection concern.
The public host entrypoints
The public wrapper components are:
ZRouterViewEmptyZRouterViewTabsZRouterViewStack
Representative wrapper sources:
zova/src/suite-vendor/a-zova/modules/a-router/src/.metadata/component/routerViewEmpty.tszova/src/suite-vendor/a-zova/modules/a-routertabs/src/.metadata/component/routerViewTabs.tszova/src/suite-vendor/a-zova/modules/a-routerstack/src/.metadata/component/routerViewStack.ts
These wrapper files matter for two reasons:
- they show that router-view hosts still enter the runtime through the normal
useController(...)wrapper path - they confirm that
controllerRefexposes the controller instance of the host, not a generic DOM ref
So even at the routed-host level, Zova keeps the same controller-oriented component model.
The shared contract: BeanRouterViewBase
The shared base host lives in:
zova/src/suite-vendor/a-zova/modules/a-router/src/lib/routerViewBase.tsxThis is the core host contract for richer routed shells.
Its main responsibilities are:
- register itself on the bean host as
$$routerView - register and deregister itself with
$router - expose host callbacks for forward-route and back-route handling
- expose
setPageMeta(...)for task-level page metadata updates - wrap routed pages in
RouterView - wrap routed pages in
KeepAlive - set vnode
keyfrom the host-computed route meta - inject the current page route into the routed vnode through host providers
A practical reading takeaway is:
- a routed page enters a host controller first, then the host decides how the page instance participates in the shell
The route-meta contract for hosts
The shared host-level route-meta types live in:
zova/src/suite-vendor/a-zova/modules/a-router/src/types/routerView.tsThe important records are:
IRouteViewRouteItemIRouteViewRouteMeta
A compact interpretation is:
tabKey= the host-level grouping identitycomponentKey= the page-instance identityfullPath= the concrete route visitkeepAlive= whether that routed instance should participate in host keep-alive inclusion
This is the point where route state becomes host state.
Host 1: routerViewEmpty
The minimal host controller lives in:
zova/src/suite-vendor/a-zova/modules/a-router/src/component/routerViewEmpty/controller.tsxZova-native role
routerViewEmpty is the minimal routed host.
It is for cases where the page should render directly without the richer tab/workspace model.
Source-confirmed runtime behavior
This host overrides render() instead of using the base BeanRouterViewBase.render() pipeline.
That means it:
- renders a plain
RouterView - creates the routed vnode directly from
component.Component - injects the current page route provider
- does not use the base keep-alive inclusion flow
- does not ask a host model to compute
tabKeyorcomponentKey
A practical reading takeaway is:
routerViewEmptyis the lowest-friction routed host and the closest thing to a shell-minimal route outlet in Zova
Host 2: routerViewTabs
The tabs host controller lives in:
zova/src/suite-vendor/a-zova/modules/a-routertabs/src/component/routerViewTabs/controller.tsxIts shared model lives in:
zova/src/suite-vendor/a-zova/modules/a-routertabs/src/model/tabs.tsZova-native role
routerViewTabs is the routed host for the workbench-style tabs model.
It does not invent a second routing system.
Instead it reinterprets normal route visits as:
- stable level-1 workspaces through
tabKey - level-2 routed page instances through
componentKey - task-level page presentation through
pageMeta - keep-alive participation through the tabs model
Source-confirmed runtime behavior
The controller itself is intentionally thin.
It delegates these responsibilities to ModelTabs:
backRoute(...)forwardRoute(...)setPageMeta(...)prepareRouteMeta(...)keepAliveInclude
That means the tabs host is really the shell-facing controller surface for the model, while ModelTabs is the real state owner.
What ModelTabs actually owns
Inside ModelTabs, the main responsibilities are:
- keep the
tabsarray as the workbench-state owner - track
tabKeyCurrentandcomponentKeyCurrent - compute
tabCurrentandtabCurrentIndex - compute
keepAliveInclude - load cached tab state when enabled
- add, update, activate, prune, and delete tabs and tab items
- update task-level
pageMeta - derive route-level host metadata from route meta and route identity
This is the main source-level fact about a-routertabs:
- the workbench model is implemented as a model bean, not as ad hoc layout-local state
How route identity becomes workbench identity
ModelTabs.prepareRouteMeta(route) does four important things:
- keeps
fullPath - computes
componentKey - computes
tabKey - computes
keepAlive
The key rules are:
- explicit
meta.componentKeyhas highest priority - otherwise a named route with
componentKeyMode: 'nameOnly'reuses the route name - otherwise the route path becomes the effective page-instance identity
- explicit
meta.tabKeygroups several route visits into one workspace - if
meta.tabKeyis absent, the model falls back tocomponentKey
That is why Router Tabs Route Meta Cookbook is really a host-behavior document as much as a route-meta document.
Why page metadata matters here
ModelTabs.updateTabItemPageMeta(...) stores page-level task state such as:
pageTitlepageDirtyformMeta
This is how task-level routed state becomes visible in workbench UI.
In the current Basic source, the Admin layout uses this for task-row icon and title behavior such as dirty state and create/edit indicators.
Current Cabloy Basic consumers
In the current public Cabloy Basic source, routerViewTabs is the actively used richer routed host.
Representative consumers:
zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/controller.tsxzova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/render.tabs.tsxzova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/controller.tsxzova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/render.tabs.tsx
The source-confirmed split is:
- Admin renders the two-level workbench meaning directly
- Web reuses the same
ModelTabsstate but presents the top level as a menu-like workspace surface
Config-sensitive differences in current Basic source
Representative config sources:
zova/src/suite/a-home/modules/home-layoutadmin/src/config/config.tszova/src/suite/a-home/modules/home-layoutweb/src/config/config.ts
In the current Basic source:
- Admin uses
scene: '',max: 6,maxItems: 3,cache: true - Web uses
scene: 'web',max: 6,maxItems: 6,cache: false
That means the shared tabs model is stable, but the layout can still choose different cache and density behavior.
Host 3: routerViewStack
The stack host controller lives in:
zova/src/suite-vendor/a-zova/modules/a-routerstack/src/component/routerViewStack/controller.tsxIts shared model lives in:
zova/src/suite-vendor/a-zova/modules/a-routerstack/src/model/stack.tsZova-native role
routerViewStack is the minimal richer host for stack-style routed caching.
Unlike routerViewTabs, it does not model a stable business workspace plus nested work items.
Instead it treats routed visits as a linear stack of page instances.
Source-confirmed runtime behavior
The controller mirrors the tabs host shape, but with a smaller contract:
backRoute(...)delegates toModelStack.backRoute(...)forwardRoute(...)delegates toModelStack.forwardRoute(...)prepareRouteMeta(...)delegates toModelStack.prepareRouteMeta(...)getKeepAliveInclude()delegates toModelStack.keepAliveInclude
There is no setPageMeta(...) override here.
That is an important difference from routerViewTabs.
What ModelStack actually owns
Inside ModelStack, the main responsibilities are:
- keep the
tabsarray as a linear stack of routed instances - compute
keepAliveInclude - add, update, delete, and prune stack items by recency
- map both
tabKeyandcomponentKeytoroute.fullPath
The crucial identity rule is:
return { tabKey: fullPath, componentKey: fullPath, fullPath };That means:
- each concrete route visit is its own stack item
- there is no level-1 workspace grouping
- there is no separate task-level page-meta model
- the host behaves more like a bounded stack of routed instances than a business workbench
Practical interpretation
routerViewStack is useful when you want:
- routed keep-alive behavior
- per-visit identity
- simple pruning by recency
- no extra workbench grouping semantics
A practical reading takeaway is:
a-routerstackis a host-level cache/stack primitive, not a tabs/workspace mechanism
If your next question is specifically about how a page author should update task-level title, dirty state, or form scene through $router.setPageMeta(...), continue with Page Meta Guide.
Current usage boundary in Cabloy Basic
In the current public Cabloy Basic source, there is no app-level layout consumer of ZRouterViewStack outside the vendor module itself.
That means the stack host is present as a reusable framework primitive, but the current public Basic layouts visibly consume routerViewTabs rather than routerViewStack.
This is a source-confirmed statement based on the current repo search surface, not a guarantee about all future editions or downstream apps.
Empty vs tabs vs stack
A useful routing-host comparison is:
| Host | Main role | Identity model | Page-meta support | Current Basic consumer shape |
|---|---|---|---|---|
routerViewEmpty | minimal routed host | no richer host model | no host-level page-meta flow | empty/minimal shell routing |
routerViewTabs | workbench host | tabKey + componentKey | yes | Admin and Web layouts |
routerViewStack | stack-style routed cache host | fullPath only | no | framework primitive, no current public Basic layout consumer |
How to choose the right mental model while reading source
Use these questions first:
When the page feels shell-minimal
Start with routerViewEmpty.
Ask:
- does this route only need plain routed output?
- is keep-alive or workspace state intentionally absent?
When the page feels like part of a workbench
Start with routerViewTabs and ModelTabs.
Ask:
- does this route need stable workspace grouping?
- does it need several inner work items?
- does page-level dirty/title/form state matter visibly?
When the page feels like a linear routed cache
Start with routerViewStack and ModelStack.
Ask:
- does each route visit stand alone?
- is fullPath-level identity enough?
- do I want pruning and keep-alive without the two-level workspace model?
Suggested source-reading path
When the question is specifically about routed hosts, read these files in order:
zova/src/suite-vendor/a-zova/modules/a-router/src/lib/routerViewBase.tsxzova/src/suite-vendor/a-zova/modules/a-router/src/component/routerViewEmpty/controller.tsxzova/src/suite-vendor/a-zova/modules/a-routertabs/src/component/routerViewTabs/controller.tsxzova/src/suite-vendor/a-zova/modules/a-routertabs/src/model/tabs.tszova/src/suite-vendor/a-zova/modules/a-routerstack/src/component/routerViewStack/controller.tsxzova/src/suite-vendor/a-zova/modules/a-routerstack/src/model/stack.ts- the active layout controller/render pair that consumes the host you care about
In the current Basic source, the most important app-level consumers are:
home-layoutadminhome-layoutweb
Common mistakes to avoid
Mistake 1: treating RouterView as the whole routed contract
In Zova, the routed host can add keep-alive, grouping, task-state, and shell-specific behavior on top of route matching.
Mistake 2: assuming router tabs are only a UI widget
In Zova, a-routertabs is a routed host plus a shared workbench-state model.
Mistake 3: assuming a-routerstack is just a smaller copy of a-routertabs
It is not.
a-routerstack uses a different identity model and does not carry the same page-meta and workspace semantics.
Mistake 4: assuming current Basic layout usage defines the full framework contract
Current Basic source confirms active consumers for routerViewTabs, but routerViewStack still exists as a reusable framework host primitive even when the current public layouts do not use it.
Edition note
This guide describes the shared routed-host architecture and the current public Cabloy Basic source.
That means the host contract is not limited to one visible Admin tab row. However, layout-level presentation and actual host selection can still vary by edition, flavor, or downstream app.
Final takeaway
If Zova Router Under the Hood explains how a route becomes routable, this page explains how that routed page is actually hosted after route resolution.
That host layer is where Zova distinguishes among plain routed output, workbench tabs, and stack-style routed caching.