Map Component
Introduction
The Map Component renders an inline Google Maps view for CXTMS app modules. It plots markers from either an explicit markers array or a dispatch route/template stops array, can draw live track paths, ignores invalid or ungeocoded coordinates, and can fire actions when a marker is clicked. It can also periodically refresh the datasource or form that owns its data.
The component loads Google Maps with the organization-specific API key from configuration. If the key is unavailable, the component shows an unavailable state instead of rendering a blank map.
YAML Structure
component: map
name: routeMap
props:
type: google
height: 520
center:
lat: 41.8781
lng: -87.6298
zoom: 10
fitBounds: true
showUnmappedNote: true
showInfoWindow: true
stops: "{{ route.stops }}"
markers:
- id: stop-1
label: "1. Warehouse"
lat: 41.8781
lng: -87.6298
address: "Chicago, IL"
color: success
text: "D1"
paths:
- label: "Driver 7 - today"
color: primary
width: 4
gapMinutes: 10
points: "{{ driverTrack.points }}"
refreshInterval: 30000
refreshHandler: driverTrack
onMarkerClick:
- setStore:
selectedStop: "{{ result }}"
Props
| Prop | Type | Default | Description |
|---|---|---|---|
type | string | google | Map provider. Only google is currently supported. |
height | number | string | 520 | Map viewport height. |
center | object | auto | Optional { lat, lng } center. Values may be numbers or numeric strings. |
zoom | number | 4 or auto | Initial zoom. A single marker defaults to zoom 14; fitted multi-marker maps clamp at zoom 15. |
markers | array | - | Explicit marker list. Takes precedence over stops. |
stops | array | - | Dispatch route/template stops. Converted to markers using each stop contact's shipping address. |
paths | array | - | Polyline definitions; each accepts points, color, width, dashed, gapMinutes, and label. |
fitBounds | boolean | true | Auto-fit the viewport to all valid markers when no explicit center is provided. |
showUnmappedNote | boolean | true | Show a caption listing markers/stops that could not be plotted. |
showInfoWindow | boolean | true | Show an info window when a marker, or a path with a label, is clicked. |
onMarkerClick | action[] | - | Actions fired after a marker click. Receives the clicked marker as result. |
refreshInterval | number | - | Periodically refresh map data, in milliseconds. Values below 1000 are ignored. |
refreshHandler | string | string[] | enclosing form | Datasource, grid, collection, list, or form signal target(s) refreshed by the timer. |
Marker Shape
id: stop-1
number: 1
label: "1. Warehouse"
lat: 41.8781
lng: -87.6298
address: "Chicago, IL"
stopType: Pickup
color: success
text: D1
lat and lng can be numbers or numeric strings. Coordinates outside valid latitude/longitude ranges are treated as unmapped. Coordinates at 0,0 are also treated as unmapped because route stops use that as a create-time default before geocoding.
Set color to a theme palette key (primary, secondary, success, warning, error, or info) or a CSS color to draw a colored teardrop pin. text replaces the sequence number inside the pin. Without color, the standard Google pin is retained.
Paths and Data Gaps
Each path contains ordered points with lat and lng; points may optionally carry an ISO recordedAt timestamp. Invalid, out-of-range, and 0,0 points are skipped.
paths:
- label: "Driver 7 - today"
color: primary
width: 4
dashed: false
gapMinutes: 10
points:
- { lat: 40.71, lng: -74.00, recordedAt: "2026-10-21T08:00:00Z" }
- { lat: 40.72, lng: -74.01, recordedAt: "2026-10-21T08:15:00Z" }
color accepts the same theme palette keys as markers or any CSS color; width defaults to 4 pixels. Set dashed: true to render the whole path dashed. When consecutive timestamped points are more than gapMinutes apart, the component draws that gap as a separate dashed warning-colored segment so missing telemetry does not look like a continuous drive. Points without parseable timestamps remain continuous.
Auto-fit includes both markers and path points. It runs initially and again when data identity changes (marker IDs, path count, or a path's first point), but not merely because live positions or path tails update. After the user pans or zooms, auto-fit remains off until that identity changes.
Periodic Refresh
The map does not own a query. refreshInterval asks the component that loaded its data to refresh through existing SDUI signals:
- component: datasource
name: driverTrack
props:
queries: []
children:
- component: map
props:
paths: "{{ driverTrack.paths }}"
refreshInterval: 30000
refreshHandler: driverTrack
Each tick dispatches both refresh and reload to every configured target. Without refreshHandler, the map targets its enclosing form using the form's reloadHandler, then refreshHandler, then form name. Outside a form there is no implicit target. A form with unsaved changes is skipped, including when addressed through one of its signal names, so polling cannot discard edits. Timers pause while the browser tab is hidden and refresh once when it becomes visible again.
Stop Mapping
When markers is not supplied, stops are converted into markers from:
dispatchRouteStopIdordispatchRouteTemplateStopIdasid- stop position as
number stopContact.nameas the marker labelstopContact.shipping.latitude/longitudeas coordinatesstopContact.shipping.addressLineas the info-window addressstopTypefor display in the info window
Deleted stops (isDeleted: true) are skipped.
Examples
Route stops map
component: map
name: dispatchRouteMap
props:
height: 560
stops: "{{ dispatchRoute.stops }}"
fitBounds: true
onMarkerClick:
- setStore:
selectedStopId: "{{ result.id }}"
Explicit markers
component: map
name: terminalMap
props:
height: 420
center:
lat: 33.749
lng: -84.388
zoom: 8
markers:
- id: atl
label: Atlanta Terminal
lat: 33.749
lng: -84.388
address: "Atlanta, GA"
- id: sav
label: Savannah Terminal
lat: 32.0809
lng: -81.0912
address: "Savannah, GA"
showUnmappedNote: false
Best Practices
- Use
markerswhen the module already has normalized location objects. - Use
stopsfor dispatch route and route template screens so marker numbering matches stop order. - Keep
fitBounds: trueunless the module needs a fixed map region. - Handle
onMarkerClickfor drill-in workflows, side-panel selection, or syncing a selected stop with the rest of the screen. - Make sure the organization has
apps.google.googleMapsApiKeyconfigured before relying on the component in production.