Routing
Internal navigation derives the /{locale}/org/{organizationId}/v2/ prefix from the ambient URL organization. Row variables named organizationId can template the path tail but cannot replace that prefix. Reusable component definitions also load from the ambient organization. A data grid may explicitly query another tenant with props.options.variables.organizationId; only a resolved positive integer overrides the ambient query organization.
Browser requests whose path does not begin with a supported locale (en-US, fr-FR, or ar-EG) redirect the complete path to the en-US prefix. This applies to deep links as well as single-segment pathsβfor example, /orders/123/events becomes /en-US/orders/123/events. Public asset directories (/images, /favicons, and /documents), static files, service-worker files, sitemap/robots files, and front-pages are excluded so asset requests remain at their original URLs. Platform-owned paths (/api, /admin, /.swa, /.auth, and /.well-known) also bypass locale redirects so Azure Static Web Apps health checks, authentication, Functions host requests, and API routes reach their handlers unchanged.
Routes define the navigation structure and access control for different screens within CXTMS. They are a crucial part of the AppModule configuration, determining how users interact with various components of the system.
Introductionβ
Routing in CXTMS establishes the hierarchical structure of the application, linking URLs to specific components and managing access permissions. This system ensures efficient navigation and proper security measures throughout the application.
When to Use Routesβ
- When defining the overall structure of your CXTMS application
- To create new screens or sections within the system
- When setting up access control for different parts of the application
- To establish relationships between different components and views
Routes Exampleβ
Transportation Managementβ
routes:
- name: "Shipments/List"
path: "shipments"
component: "Shipments/ListComponent"
props:
title:
en-US: "Shipments"
requiredPermissions:
- "Shipments/Read"
children: # Nested routes
- name: "Shipments/Details"
path: ":id" # Route path with a dynamic parameter
component: "Shipments/DetailsComponent"
Route Fieldsβ
| Field | Description |
|---|---|
name | Unique identifier for the route. Shown on the unauthorized page. |
path | URL path, relative to the organization prefix. Supports :param segments. |
component | Name of the component to render (e.g. Shipments/DetailsComponent). |
props | Route metadata read by the shell (see below). Not passed to the component. |
children | Nested routes. The child path is appended to the parent path. |
platforms | Optional list of platforms (web, mobile). Defaults to both. |
Route propsβ
The route's props object holds metadata that the application shell reads. It is not merged into the component's variables.
| Prop | Type | Description |
|---|---|---|
title | string / localized | Page title and browser tab title. Supports template expressions. |
requiredPermissions | string[] | The user must hold all listed permissions, otherwise the unauthorized page is shown. |
permissions | string[] | Alias of requiredPermissions (used when requiredPermissions is not set). |
permission | string | Single permission. Honored only when the route is opened from a notification link β use requiredPermissions for page access control. |
icon | string | Icon shown for the route in global search. |
isSearchable | boolean | Set to false to hide a top-level route from global search. Default true. |
Permissions must be declared inside props. A permission or requiredPermissions key placed at the route's top level is not checked by the web application.
Component Variables on a Routeβ
A component rendered by a route receives these variables:
| Variable | Source |
|---|---|
| Route parameters | :param segments of the matched path. Always strings. |
| Query parameters | The URL query string. Numeric values are converted to numbers losslessly. |
organizationId | The organization in the current URL. |
locale | The locale in the current URL. |
currentUser | The signed-in user, including permissionSet. |
Because route parameters are strings, cast them in queries: "{{ number orderId }}".
Path Matchingβ
- Paths are matched case-insensitively against the part of the URL after
/{locale}/org/{organizationId}/v2/. Leading and trailing slashes are ignored. - Routes are evaluated in order, and the first match wins.
- A
:paramsegment matches any single path segment except the literalcreate, unless the route's own path containscreate. This letsorders/createandorders/:orderIdcoexist. - Nesting resolves one level: a child's full path is
parent.path/child.path. For deeper hierarchies, give the grandchild route its full path explicitly instead of relying on nestedchildren. - If no route matches, a "not found" page is shown. If the user lacks a required permission, an "unauthorized" page is shown.
How to Create Routesβ
- Open your AppModule configuration file
- Locate the
routessection - Add a new route entry with the required fields:
routes:
- name: "ExampleRoute"
path: "example"
component: "Example/ExampleComponent"
props:
title:
en-US: "Example Route"
requiredPermissions:
- "ExampleModule/Read"
- For nested routes, add them under the
childrenproperty of a parent route - Save your configuration file
Navigating Between Routes and Dialogsβ
A component can be opened in two ways:
- As a page β through a route, using the
navigateaction or the redirect component. - As a dialog β on top of the current screen, using the
dialogaction. The component is referenced by the same name used in the route'scomponentfield.
Design detail and edit components so they work in both contexts: finish with navigateBackOrClose, which closes the dialog (returning result to the caller) or navigates back when the component is a page.
# Open as a page
- navigate: "orders/{{ orderId }}"
# Open the same component as a dialog
- dialog:
component: "Orders/Detail"
props:
title: { en-US: "Order Details" }
orderId: "{{ orderId }}" # dialog props become the component's variables
onClose:
- if: "{{ result }}"
then:
- refresh: ordersGrid
Route Matching for Linked Notifications and Dialogsβ
CXTMS route matching supports entity-style paths such as Order/12345 or Terminal/42. When a notification includes entityType and entityId, the UI resolves the target route by matching the generated path (entityType/entityId) against configured app routes. If a matching route exists and the current user has all route permissions, the target component opens in a dialog with route parameters converted to numbers when possible.
The web notification bell only lists notifications delivered through the Web channel. Push-only notifications remain in the mobile notification experience and do not appear in the web bell. To make a linked notification available on both renderers, include both Web and Push in its delivery channels.
Notification access checks read these shapes from the route's props (first match wins):
requiredPermissions: ["Terminal/Read"]permissions: ["Terminal/Read", "Port/Read"]permission: "Terminal/Read"
For notification dialogs, matched route parameters are converted to numbers only when the conversion is lossless. (On a regular page, route parameters stay strings β see Component Variables on a Route.) Ordinary values such as 42 become numbers, while leading-zero identifiers, exponential notation, hexadecimal strings, whitespace-padded values, and integers larger than JavaScript's safe range remain strings. This prevents tracking numbers and external IDs from being rounded or rewritten when passed into a linked screen or dialog.
Use dynamic route parameters for entity detail screens so notifications can deep-link cleanly:
routes:
- name: "Terminals/Detail"
path: "Terminal/:terminalId"
component: "Terminals/TerminalDetail"
props:
title: "Terminal {{ terminalId }}"
requiredPermissions:
- "Terminal/Read"
Mobile back navigationβ
The mobile renderer keeps an application-level route history and dispatches stack navigation actions through the container ref. navigateBack returns to the previous SDUI screen when one exists and falls back cleanly when the app was cold-started or the history is empty. This preserves nested stack behavior without requiring module authors to change route definitions.
On phones, modal routes use the native iOS-style slide-from-bottom transition. Tablets continue to use the standard screen transition. This renderer behavior requires no route YAML changes.
Reactive Page Titlesβ
Route props.title and root component displayName can be template expressions. The screen title and browser document.title are evaluated against the app module store and update when store values change.
routes:
- name: "Orders/Detail"
path: "orders/:orderId"
component: "Orders/Detail"
props:
title: "Order {{ order.orderNumber }}"
Prefer route props.title for route-specific labels and component displayName for reusable component defaults. Unauthorized-route messages use the route name/path rather than parsing templated titles, because store data may not be available when access is denied.
Best Practicesβ
- Use descriptive and consistent naming conventions for route names
- Keep URL paths (the
pathproperty) short and meaningful - Organize routes hierarchically to reflect the structure of your application
- Always set
props.requiredPermissionsto ensure proper access control - Pass data to a routed component through
:parampath segments or query parameters, not routeprops - Use entity-style detail paths (
Order/:orderId) so notification links can open them as dialogs