--- title: Model Relay category: Infusion --- Using the Infusion Model Relay system, you can supply declarative configuration which specifies rules for keeping multiple pieces of model state around the component tree automatically up to date with each other's changes. We sometimes use the term _**model skeleton**_ to refer to a connected set of models around the component tree which are related together by relay rules. This term illustrates that these models form an inner core which arrive at a set of mutually consistent model values, before they start to notify listeners out in the periphery of each component (generally, in its view layer). The system might make several passes over a model skeleton in order to satisfy all the relay rules, and then update all [`modelListeners`](ChangeApplierAPI.md#model-listener-declaration) as a single operation — presenting them with a consistent snapshot of the state of the entire skeleton all at once. This guarantees that no model listener ever sees a model value which is inconsistent with the relay rules which are meant to be keeping them in step, which greatly enhances the reliability and clarity of the application design. Every Infusion component descended from the grade `fluid.modelComponent` (a model-bearing component) supports a configuration area named `modelRelay` in which these rules can be defined, as well as allowing a short-form _implicit syntax_ in the `model` area which can be used where the model values to be synchronised are identical at each end of the relationship. ### Two styles for configuring model relay There are two styles of configuration for setting up model relay — firstly, using the _[implicit syntax](#implicit-model-relay-style)_ which just consists of [IoC References](IoCReferences.md) from the model configuration for one model-bearing component to another — that is, in the component's configuration under the top-level `model` entry. Secondly, the _[explicit syntax](#explicit-model-relay-style)_ which involves an entry in the component's top-level `modelRelay` block expressing a more complex rule, most likely involving some form of [Model Transformation](ModelTransformationAPI.md) to apply during the relay process. Both of these styles will set up a permanent and bidirectional relationship between the two models at the ends of the relay — the relationship will be established as the components construct (during the _[initial transaction](#the-initial-transaction)_), and will persist until one of the components at the endpoints is destroyed. ### How model relay updates propagate A set of models which are linked by relay rules are called a _**model skeleton**_. Whenever an update (via its [ChangeApplier](ChangeApplier.md)) is received to any model which is part of the skeleton, a _**transaction**_ begins in order to propagate it to all the related models with which it should be synchronised. The framework will keep propagating the change to all the models in the skeleton until they have all been brought up to date — however, these updates will initially occur privately within the transaction and any external listeners (`modelListener`s) will not be notified during the update process. Once the framework has computed final synchronised values for all of the models in the skeleton, the transaction ends, and then all listeners (via their respective ChangeAppliers) will be notified all at once. If any of these listeners have a [`priority`](Priorities.md) field attached to the listener declaration, the framework will sort all of these listeners globally across the entire model skeleton impacted by the change, before starting to notify them. ### The initial transaction Whenever a new model-bearing component (or an entire tree of model-bearing components) constructs, there will be a particular, large style of update transaction known as an **initial transaction**. This is very similar to any other synchronisation transaction caused by a [model relay update](#how-model-relay-updates-propagate), although it will typically involve more data since all of the initial values of all the involved models must be taken into account — these result from any of the normal sources for component configuration, including [defaults](https://github.com/fluid-project/infusion/blob/infusion-1.5/src/framework/core/js/Fluid.js#L1519-L1539), user-supplied values, [distributed](IoCSS.md) options, etc. At the end of the initial transaction, any [declaratively registered listeners](ChangeApplierAPI.md#model-listener-declaration) will observe all of the new models go through the transition from holding their primordial value of `undefined` to holding their correct synchronised initial values. As with any other relay update transaction, this will appear to all the observers to occur in a single step even though from the point of view of the framework it may be a complex process involving many passes through the components. You can control which components have the default values for their models honoured and which ignored during the initial transaction, by using the directives `forward` and `backward` in the `modelRelay` block - these are discussed in the section [Controlling Propagation Through a Relay Rule](#controlling-propagation-through-a-relay-rule). ## Implicit model relay style This is the most straightforward style for setting up model relay. This takes the form of a simple [IoC Reference](IoCReferences.md) between one component's model and other. Here is a component which has a child component which sets up a model relay relationship with it: ```javascript fluid.defaults("examples.implicitModelRelay", { gradeNames: "fluid.modelComponent", model: { parentValue: 3 }, components: { child: { type: "fluid.modelComponent", options: { model: { // implicit relay rule setting up synchronisation with one field in parent's model childValue: "{examples.implicitModelRelay}.model.parentValue" } } } } }); var that = examples.implicitModelRelay(); // next line logs 3 - parent's value has been synchronised to child on construction console.log(that.child.model.childValue); ``` The IoC expression `{examples.implicitModelRelay}.model.parentValue` here refers from one field of the child's model called `childValue` to one field of the parent's model called `parentValue`. This sets up a permanent model relay linking these two fields. From the construction point onwards, the framework will ensure that all updates made to one field will be reflected in the other. The framework will naturally ensure that if either of the components at the endpoint are destroyed, the relay will be torn down. For example, here are some further pieces of code we could write, following on from the above example — these use the programmatic [ChangeApplier API](ChangeApplierAPI.md) although in practice it is desirable to express as many such updates as declaratively as possible: ```javascript that.applier.change("parentValue", 4); // update the parent's model field to hold the value 4 console.log(that.child.model.childValue); // 4 - The child's model value has been updated that.child.applier.change("childValue", 5); // update the child's model to hold the value 5 console.log(that.model.parentValue); // 5 - The parent's model value has been updated ``` ## Explicit model relay style This style is used when we require a [Model Transformation](ModelTransformationAPI.md) rule to mediate between the updates synchronising one model value with another, or more control over the occasions when the updates occur. The simple implicit style is only capable of "moving" the same value between one path and another. Sometimes different models choose different strategies for representing "the same value" — for example, one component might represent a sound volume level on a scale of 0-100, whereas another might use a range of 0-1. The framework is capable of accommodating this kind of difference in viewpoint by allowing the user to explicitly list a transformation rule relating one model's instance of a value with another. This is done using the `modelRelay` section of a component's top-level options. Here is the layout of this options section: ### Layout of top-level `modelRelay` section of `fluid.modelComponent` options The `modelRelay` options block may take one of the following three forms - * A single [`modelRelayBlock`](#modelrelayblock-layout) holding a single model relay rule - _OR_ * An array of `modelRelayBlock` sections - _OR_ * A map of keys (namespaces) to `modelRelayBlock` sections The first and third cases are disambiguated by looking for a member of the block named `target` which holds a `String` value - required for a single `modelRelayBlock`. ### `modelRelayBlock` layout
Option name | Type | Description | Example |
---|---|---|---|
source (usual, but not required) |
String (EL Reference or short local path) | The source model path to be linked by this relay rule. This option can be omitted if all arguments to the transform are presented as IoC references in its definition — as, for example, with `fluid.transforms.free`. | "volume" / "{someComponent}.model.volume" |
target (required) |
String (EL Reference or short local path) | The target model path to be linked by this relay rule. This option must always be present. | "volume" / "{someComponent}.model.volume" |
singleTransform |
JSON (single Model Transformation rule) |
A short form which can be used where the transformation consists of just a single Model Transformation
transform rule. Use either this or transform
|
{
type: "fluid.transforms.linearScale",
factor: 100
}
|
transform |
JSON (full Model Transformation document) |
A long form of singleTransform which allows any valid Model Transformation document to be
used to mediate the relay. This is an infrequently used form.
|
See the list of available transformation functions for more information. |
forward (optional) |
sourceFilterRecord |
Control the situations in which the forward
leg of the transform operates - by listing one or more change sources
which must match (includeSource ) or not match (excludeSource ) in the current
transaction.
|
{excludeSource: "init"} |
backward (optional) |
sourceFilterRecord |
Control the situations in which the backward
leg of the transform operates - by listing one or more change sources
which must match (includeSource ) or not match (excludeSource ) in the current
transaction.
|
{excludeSource: "init"} |
namespace (optional) |
String |
Identifies this model relay rule uniquely within its parent component. This namespace may be used to override the relay
rule from a derived grade, or to control the priority of one relay rule with respect to
another by using the priority field. Compare with the similar uses of namespaces within Infusion events, model listeners, etc. As with `modelListener`, this value
will be taken from the key of the modelRelayBlock if it is in the "map of keys" form and the `namespace`
member is absent.
|
"resolveCentre" |
priority (optional) |
Priority (Number|String) |
In complex scenarios where multiple model relay rules might be activated in response to a particular invalidation of the model, it can be useful to be able to control which one is picked first. Depending on the implementation of the model relay rules, very different model states might result from the final relay resolution, and only one of these might be acceptable to the user. | "after:resolveCentre" |