ChangeApplier API

This documentation is currently being moved to our new documentation site.

Please view or edit the documentation there, instead.

If you're looking for Fluid Project coordination, design, communication, etc, try the Fluid Project Wiki.

ChangeApplier API

Registering interest in model changes using a ChangeApplier

Declarative style

The declarative style for registering interest in change events uses an entry in the modelListeners options area of a modelRelayComponent. These listeners are attached to the applier during the construction process of the entire component (and its surrounding tree) and so will therefore become notified as part of the initial transaction - they will therefore get to observe the model changing state from its primordial value of undefined to holding their initial resolved value. This is the recommended way of listening to model changes using the ChangeApplier system.

Each record in the modelListeners block has the format <modelPathReference>: <modelListener declaration>. The left and right hand sides of this definition will be explained in the subsequent sections:

Model path references

A <modelPathReference> has the form:

Syntax definition for <modelPathReference> - the key in modelListeners options block for a modelRelayComponent

Syntax definition for <modelPathReference> - the key in modelListeners options block for a modelRelayComponent

Syntax

Meaning

Examples

Simple String

Reference to a model path in this component

"modelPath" / "modelPath.*" / ""/ "*"

IoC reference

Reference to a model path in another component

"{otherComponent}.model.modelPath", "{otherComponent}.model.modelPath.*", "{otherComponent}.model", "{otherComponent}.model.*"

The 4 examples presented in the "Examples" column are parallel for the two cases - they respectively match changes occurring in the same parts of the target model, only in the first row they match into the model attached to this component (the same one in which the modelListeners record appears) and in the second row they match into the model attached to another component - one referenced by the Context Expression otherComponent.

Model listener declaration

A model listener declaration block has exactly the same form and meaning as any of the record types supported by Invokers and Listeners - including the one-string compact syntax documented with Invokers. The only difference is that an extra context name is available in this block by the name of change. This is bound to the particular change event which triggered this listener. This context behaves as an object with the following fields:

Members of the {change} object bound in a model listener declaration

Members of the {change} object bound in a model listener declaration

Member

Type

Description

{change}.value

Any type

The new value which is now held at the model path matched by this model listener block

{change}.oldValue

Any type

The previous value which was held at the matched model path, before it was overwritten by the change being listened to

{change}.path

String

The path at which this change occurred. In general this will be the same as the path registered as the modelPathReference for this block - however it may be one segment longer if a wildcard path was used (see next section)

 

Wildcards in model path references

The last path segment of a model path reference may or may not be "*". Whether it is "*" or not, the reference matches exactly the same set of changes - the only difference is in how they are reported. A path reference of "things" will match all changes occurring below this path segment, and report all those occurring within a single transaction as a single change. A path reference of "things.*" will match the same changes, but will report one change for each immediately nested path segment touched by the changes. For example, the following definition will log just one 

fluid.defaults("examples.pathExample1, { gradeNames: ["fluid.modelRelayComponent", "autoInit"], modelListeners: { things: { funcName: "console.log", args: ["{change}.value", "{change}.path"] } } });   var that = examples.pathExample1(); that.applier.change("things", {a: 1, b: 2}); // this logs {a: 1, b: 2}, "things" to the console

However, the following example which just differs in the listener path (swapping "things" for "things.*") will log two changes:

 

fluid.defaults("examples.pathExample2, { gradeNames: ["fluid.modelRelayComponent", "autoInit"], modelListeners: { "things.*": { funcName: "console.log", args: ["{change}.value", "{change}.path"] } } });   var that = examples.pathExample2(); that.applier.change("things", {a: 1, b: 2}); // logs 2 lines // Line 1: 1, "things.a" // Line 2: 2, "things.b"

The standard way to be notified of any changes to the model in a single notification is to use a model path reference consisting of the empty string "". Use of "*" will react to the same changes, but will report multiple notifications for compound modifications as in the above example.

It is not currently possible to supply more than one wildcard segment per path reference, or to supply the wildcard at any position in the string other than as the last path segment.

Programmatic style

The programmatic style for registering interest in model changes uses an API exposed by the ChangeApplier on its member modelChanged that is very similar to that exposed by a standard Infusion Event - the difference is that the addListener method accepts an extra 1st argument, pathSpec - this holds the same model path reference documented in the previous section on declarative binding:

applier.modelChanged.addListener(pathSpec, listener, namespace) applier.modelChanged.removeListener(listener)

This style of listening to changes is not recommended. Since such a listener can only be attached once a component and its applier have been fully constructed, it will miss observation of the initial transaction as well as any other model changes that have occurred up to the point where it is registered. This implies that the state of the model that it sees cannot be fully predicted without a knowledge of the entire surrounding of the component tree. 

The listener is notified after the change (or set of coordinated changes) has already been applied to the model - it is too late to affect this process and so this event is not preventable. The signature for these listeners is

function listener(value, oldValue, pathSegs, changeRequests)

Parameter

Description

Parameter

Description

value

The new (current) model value held at the path for which this listener registered interest

oldValue

A "snapshot" of the previous model value held at that path

pathSegs

An array of String path segments holding the path at which value and oldValue are/were held

changeRequests

A single ChangeRequest object or an array of them (see below) which were responsible for this change (may be empty)

Users will in many cases only be interested in the first argument in this signature.

Firing a change using a ChangeApplier

Declarative style

The declarative style for firing model changes involve a kind of IoC record that is supported in various places in component configuration, in particular as part of the definition of both Invokers and Listeners of an IoC-configured component. This style of record is recognised by its use of the special member changePath which determines which path in which component model will receive the change.

Change record for firing changes by declarative binding

Change record for firing changes by declarative binding

Member

Type

Description

 

changePath

<model path reference> (String)

The reference to the model path in a model somewhere in the component tree where the change is to be triggered. This has the same syntax as the model path references documented above for declarative listening, only wildcard forms are not supported. Four examples: "modelPath" / "" / "{otherComponent}.model.modelPath" / "{otherComponent}.model"

 

value

Any type

The value which should be stored at the path referenced by changePath. If this contains compound objects (built with {}), these will be merged into the existing values in the model. If this contains arrays (built with []) these will overwrite existing values at that path.

 

type

String (optional)

If this holds the value DELETE, this change will remove the value held at changePath. In this case, value should not be supplied. This is the recommended way of removing material from a model - it has the effect of the delete primitive of the JavaScript language. Sending changes holding a value of null or undefined does not have the same effect, as per the JavaScript language spec.