The Traits system is Terria's way of managing a priority order for state. For example, we might have a default opacity property from a config file that should be overriden by a user provided value. This article will go through how Traits are made, how the priority system works and how updates happen in the UI.
For base traits such as BaseMapsTraits
, we extend ModelTraits. However when composing multiple traits, we use the mixTraits
function. For example, ArcGisTerrainCatalogItemTraits
is composed out of UrlTraits, MappableTraits, CatalogMemberTraits
.
When creating a new property within a Traits class, we use decorators to ensure that the field takes part in the Traits system.
Stratums are how Terria determines which value a Trait should resolve to.
There are 4 strata types
- Defaults
- Loadable
- Definition
- User
Each type can have multiple strata - see StratumOrder.ts
There are 5 common strata - these exist for every model
- Defaults
default
- Definition
underride
definition
override
- User
user
This is the lowest priority stratum best thought of as a sensible fallback value. For example opacity
(in OpacityTraits.ts
)
@primitiveTrait({
type: "number",
name: "Opacity",
description: "The opacity of the map layers."
})
opacity: number = 0.8;
These should only be used in Trait definitions.
underride
values can be used to "override" the default
stratum values - but not definition
values. It can be thought of setting a new "default" value for a Trait
- as definition
stratum shouldn't be changed.
Some example usages of underride
- Copying
itemPropertiesById
,itemPropertiesByType
,itemProperties
, to nested groups or nested references - that is, when a group or reference is loaded, if there are nested groups or nested references. - Setting
isExperiencingIssues = true
for models which have configuration issues
Values provided when a model is created
Values from initialization files. For example a WMS item with opacity = 1
.
{
"catalog": [
{
"type": "wms",
"name": "A WMS layer",
"url": "some-wms-server.com/layer",
"opacity": 1
},
...
],
...
}
Values for models created programmatically by dynamic groups - for example WebMapServiceCatalogGroup
or CkanCatalogGroup
...
override
values can be used to "override" the definition
stratum values and lower level strata (that isn't a result of a user interaction).
Some use cases:
- To override invalid
definition
values - Apply
itemProperties
This is the highest priority stratum - it represents user-driven values. This strata should only be used for values changed by user-interaction (for example the opacity
slider)
Loadable strata sit between the defaults
and definition
strata. It represents values which are loaded from external sources - for example WMS GetCapabilities
.
WebMapServiceCatalogItem
uses the loadable stratum WebMapServiceCapabilitiesStratum
to set configuration from the WMS server's GetCapabilities
.
This includes layers
, styles
, legends
, ...
This means that we aren't required define all this configuration in definition
manually (eg init files) - as sensible values can be computed based on GetCapabilities
. But, as Loadable strata sites below definition
, we can "override" these values by setting values in definition
.
It is important to note that there are many Loadable strata - and models can have multiple.
To start, let's assume that we have an opacity
trait with a value of 0.5 loaded through configuration. As it is loaded through configuration, it is placed within the Definition
stratum.
When a user interacts with an opacity slider and sets its new value to 0.42, setTrait(CommonStrat.user, "opacity", 0.42)
is invoked within an action. This triggers the computed value to be recalculated and the new value of 0.42
is picked up as the value in the User stratum has a higher priority than the Definition stratum.
As the computed value is updated, other parts of the UI that observe that value are automatically rendered again reflecting the updated value.