Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 4.0
{section} {column:width=50%} h2. ChangeApplier API This section explains and documents the various Javascript API calls for instantiating and working with ChangeAppliers. h3. Instantiating a ChangeApplier Instantiating a ChangeApplier is extremely simple. {code:javascript}
Wiki Markup
Section
Column
width50%

ChangeApplier API

This section explains and documents the various Javascript API calls for instantiating and working with ChangeAppliers.

Instantiating a ChangeApplier

Instantiating a ChangeApplier is extremely simple.

Code Block
javascript
javascript

var applier = fluid.makeChangeApplier(model);
{code} {code:javascript}
Code Block
javascript
javascript

var applier = fluid.makeChangeApplier(model, options);
{code}

Please

see

[

Options

|#optionsdesc]

below

for

more

information

about

the

new

{{

options

}}

parameter.

Note

that

this

new

parameter

is

independent

of

the

new

Transaction

support

(which

also

uses

options).

The {{makeChangeApplier}} call takes a single argument, which is the model object (the root of a tree of plain Javascript objects) to which this applier is to be attached. Note that by standard model semantics, whilst any subobjects and properties under this tree can and will change asynchronously, the essential identity of this model tree is defined by this exact object handle {{model}}. Therefore to establish agreement amongst model citizens and appliers about _which_ model is being talked about, this object handle must be preserved, whatever happens to the object tree beneath it. The Fluid base framework contains various utility functions which help to make this easy - for example {code:javascript}

The makeChangeApplier call takes a single argument, which is the model object (the root of a tree of plain Javascript objects) to which this applier is to be attached. Note that by standard model semantics, whilst any subobjects and properties under this tree can and will change asynchronously, the essential identity of this model tree is defined by this exact object handle model. Therefore to establish agreement amongst model citizens and appliers about which model is being talked about, this object handle must be preserved, whatever happens to the object tree beneath it. The Fluid base framework contains various utility functions which help to make this easy - for example

Code Block
javascript
javascript

fluid.clear(model);
{code}

is

a

call

which

unlinks

all

properties

from

the

supplied

model,

preparing

it

for

a

wholesale

change.

A

corresponding

useful

call

is

{

Code Block
:
javascript
}
javascript

fluid.model.copyModel(model, newModel)
{code} {column} {column} {panel:title= On This Page| borderStyle=solid| borderColor=#566b30| titleBGColor=#D3E3C4| bgColor=#fff} {toc:minLevel=2|maxLevel=5} {panel} {panel:title=See Also|borderStyle=solid|borderColor=#321137|titleBGColor=#c1b7c3|bgColor=#fff} * [ChangeApplier] * [Infusion Event System] {panel} {include:Still Need Help panel} {column} {section} which transfers all the state from {{newModel}} onto {{model}} - whilst still preserving the identity of {{model}} as {{model}} - and hence its association with any particular ChangeApplier. These two calls, {{clear}} followed by {{copyModel}} often occur together (and will be automated in a future version of the ChangeApplier). After its construction, the particular model object to which a ChangeApplier is bound is available at {{applier.model}}. Version 1.3 of Infusion includes a [Sneak Peek|Infusion13:Component Status] view of our new transactional ChangeApplier. The ChangeApplier supports transactions by default, and can be configuring using an optional {{options}} parameter: {code:javascript}
Column
Panel
borderColor#566b30
bgColor#fff
titleBGColor#D3E3C4
titleOn This Page
borderStylesolid
Table of Contents
minLevel2
maxLevel5
Panel
borderColor#321137
bgColor#fff
titleBGColor#c1b7c3
titleSee Also
borderStylesolid
Include Page
Still Need Help panel
Still Need Help panel

which transfers all the state from newModel onto model - whilst still preserving the identity of model as model - and hence its association with any particular ChangeApplier. These two calls, clear followed by copyModel often occur together (and will be automated in a future version of the ChangeApplier).

After its construction, the particular model object to which a ChangeApplier is bound is available at applier.model.

Version 1.3 of Infusion includes a Sneak Peek view of our new transactional ChangeApplier. The ChangeApplier supports transactions by default, and can be configuring using an optional options parameter:

Code Block
javascript
javascript
var applier = fluid.makeChangeApplier(model, options);
{code}

For

...

more

...

information

...

see

...

the

...

fluid:Transactional

...

ChangeApplier

...

section

...

below.

...

Firing

...

a

...

change

...

using

...

a

...

ChangeApplier

...

There

...

are

...

two

...

calls

...

which

...

can

...

be

...

used

...

to

...

fire

...

a

...

change

...

request

...

-

...

one

...

informal,

...

using

...

immediate

...

arguments,

...

and

...

a

...

more

...

formal

...

method

...

which

...

constructs

...

a

...

concrete

...

ChangeRequest

...

object.

...

:}
Code Block
javascript
javascript
applier.requestChange(path, value, type)
{code}
||Parameter||Type||Description
|{{path}}|{{string}}|An EL path into the model where the change is to occur.
|{{value}}|{{object}}|An object which is to be either updated (or added if necessary), or removed from the model at path 
{{path}}
|{{type}}|(optional) {{"ADD"}} or {{"DELETE"}}|A key string indicating whether this is an ADD request (the default) or a DELETE request (a request to unlink a part of the model)

{code:javascript}

Parameter

Type

Description

path

string

An EL path into the model where the change is to occur.

value

object

An object which is to be either updated (or added if necessary), or removed from the model at path
path

type

(optional) "ADD" or "DELETE"

A key string indicating whether this is an ADD request (the default) or a DELETE request (a request to unlink a part of the model)

Code Block
javascript
javascript
applier.fireChangeRequest(changeRequest)
{code}
{{requestChange}} and {{fireChangeRequest}} reach exactly the same implementation - the only difference is in the packaging of the arguments. For {{requestChange}} they are spread out as a sequence of 3 arguments, whereas for {{fireChangeRequest}}, they are packaged up as named fields ({{path}}, {{value}} and {{type}}) of a plain Javascript object. Such an object is called a "ChangeRequest" and is a convenient package for these requests to pass around in in an event pipeline.

h3. Registering interest in a ChangeApplier

Currently ChangeAppliers support two types of listeners (to be expanded). 

{anchor:guarddesc}
h4. Guard Listeners.
The first type, called *guard listeners*, are notified of an upcoming change request _before_ it occurs. Guards can be registered and deregistered at the path {{guards}} with a call to {{addListener}}:

{code:javascript}

requestChange and fireChangeRequest reach exactly the same implementation - the only difference is in the packaging of the arguments. For requestChange they are spread out as a sequence of 3 arguments, whereas for fireChangeRequest, they are packaged up as named fields (path, value and type) of a plain Javascript object. Such an object is called a "ChangeRequest" and is a convenient package for these requests to pass around in in an event pipeline.

Registering interest in a ChangeApplier

Currently ChangeAppliers support two types of listeners (to be expanded).

Anchor
guarddesc
guarddesc

Guard Listeners.

The first type, called guard listeners, are notified of an upcoming change request before it occurs. Guards can be registered and deregistered at the path guards with a call to addListener:

Code Block
javascript
javascript
applier.guards.addListener(pathSpec, guard, namespace)
{code}
||Parameter||Type||Description
|{{pathSpec}}|{{string}} or {{Object}}|An EL expression, possibly with wildcards ({{*}} in place of a path component) which matches the set of EL paths, which on receiving a change request, will trigger this guard.\\
\\
The {{pathSpec}} can also be an Object with the following properties:\\
{code:javascript}

Parameter

Type

Description

pathSpec

string or Object

An EL expression, possibly with wildcards (* in place of a path component) which matches the set of EL paths, which on receiving a change request, will trigger this guard.

The pathSpec can also be an Object with the following properties:

Code Block
javascript
javascript
{
    path: an EL expression
    priority: an integer
    transactional: boolean  (see Transactional Listeners below)
}

...


The priority will affect when the listener is fired: All listeners will be sorted according to priority and fired in order. A higher number implies a higher priority. The default if unspecified will be the highest possible priority.

guard

function

A function pointer holding a guard

namespace

string

(Optional) - a namespace key, which is used to scope additions and removals of this guard (see Infusion Event System for interpretation of event namespaces)

A guard may be removed with a call to

Code Block
javascript
javascript
applier.guards.removeListener(guard)
{code}
where {{guard}} holds either the original function reference, or else the {{string}} value supplied as the {{namespace}}.

A guard listener is just a function with a particular signature - for example, {{guard}} could be implemented as follows:

{code:javascript}

where guard holds either the original function reference, or else the string value supplied as the namespace.

A guard listener is just a function with a particular signature - for example, guard could be implemented as follows:

Code Block
javascript
javascript
function guard(model, changeRequest) {
    if (changeRequest.value === null) {
        return false;
        }
    }
}
{code}

The

...

behaviour

...

of

...

this

...

guard

...

is

...

to

...

reject

...

any

...

incoming

...

change

...

which

...

would

...

appy

...

a

...

null

...

value

...

to

...

the

...

model

...

-

...

by

...

making

...

a

...

false

...

return,

...

the

...

entire

...

change

...

cycle

...

which

...

triggered

...

it

...

would

...

be

...

cancelled,

...

since

...

guards

...

have

...

the

...

semantics

...

of

...

preventable

...

events

...

(see

...

Infusion

...

Event

...

System

...

for

...

the

...

different

...

event

...

categories).

...

The

...

arguments

...

supplied

...

to

...

a

...

guard

...

are

...

as

...

follows:

...

Parameter

Description

model

The overall model attached to the ChangeApplier with which this guard is registered

changeRequest

The ChangeRequest object with which this request was started

modelChanged listeners

Registration and deregistration of modelChanged listeners is just as for guards -

Code Block
javascript
javascript
applier.modelChanged.addListener(pathSpec, listener, namespace)
applier.modelChanged.removeListener(listener)
{code}

The

...

signature

...

and

...

timing

...

of

...

the

...

listener

...

is

...

different

...

to

...

guards.

...

Unlike

...

a

...

guard,

...

the

...

listener

...

is

...

notified

...

after

...

the

...

change

...

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

...

:}
Code Block
javascript
javascript
function listener(model, oldModel, changeRequest)
{code}
||Parameter||Description
|model|The overal model attached to the ChangeApplier with which this listener is registered
|oldModel|A "snapshot" of the previous model condition - created as if by {{fluid.copyModel}}. 
|changeRequest|The ChangeRequest object with which this request was started

{anchor:optionsdesc}
h3. Options

In version 1.3, the ChangeApplier now supports an optional {{options}} parameter that allows users to configure how the Applier works. The currently supported options are:

||Name||Description||Values||Default||
|{{cullUnchanged}}| This option allows users to configure a ChangeApplier to _not_ fire a {{modelChanged}} event if a change request doesn't, in fact, modify the model.|boolean |{{false}} |

{HTMLcomment

Parameter

Description

model

The overal model attached to the ChangeApplier with which this listener is registered

oldModel

A "snapshot" of the previous model condition - created as if by fluid.copyModel.

changeRequest

The ChangeRequest object with which this request was started

Anchor
optionsdesc
optionsdesc

Options

In version 1.3, the ChangeApplier now supports an optional options parameter that allows users to configure how the Applier works. The currently supported options are:

Name

Description

Values

Default

cullUnchanged

This option allows users to configure a ChangeApplier to not fire a modelChanged event if a change request doesn't, in fact, modify the model.

boolean

false

Wiki Markup
{htmlcomment}
h2. Working with composite models

An extremely useful affordance of Fluid's "transparent" model for models, is that these may participate very straightforwardly in aggregation. Models may even at one time be part of multiple, perhaps "ad hoc" assemblages of models into wider address spaces, or "super-models" - since their update and read semantics are clear and uniform, they need no special notification or modification of their role. All that is required is that updates are properly routed through the particular ChangeApplier instance which is attached to them. In order to help in the task of creating these model assemblages, {{DataBinding.js}} includes a dedicated call {{fluid.assembleModel}}:

{code:javascript}
fluid.assembleModel(modelSpec)
{code}

{{modelSpec}} is a model specification object, which consists of a map of path keys to records {{model, applier}}, for example:
{code:javascript}
{
"path1": {
    model: subModel1,
    applier: subApplier1
    },
"path2": {
    model: subModel2,
    applier: subApplier2
    }
}
{code}
represents a {{modelSpec}} object, where {{subModel1}}, {{subModel2}} are two different models, and {{subApplier1}} and {{subApplier2}} are two appliers which have been attached to them. The return value from {{assembleModel}} is a fresh {{model, applier}} pair which is ready for further aggregation. Note that such a "super-Applier" only fulfils a "reduced contract" - the only one of the ChangeApplier methods it supports is {{fireChangeRequest}} - any other methods (adding and removing listeners, etc.) must be targetted at the original individual appliers. However, the "reduced contract" is sufficient for passing the ChangeApplier in to an application of the [Fluid Renderer|Infusion13:Fluid Renderer - Background] in "autoBind" mode.
{

...

htmlcomment}

...

Anchor

...

transactional
transactional

Sneak Peek: Transactional ChangeApplier

As of version 1.3 of Infusion, the ChangeApplier now supports transactions. Note that this feature is in Sneak Peek status, and so its APIs will change. We encourage users to provide feedback by emailing our infusion-users mailing list.

The functioning of the transactional ChangeApplier can be configured using the following options:

Name

Description

Values

Default

thin

By default, transactions create a copy of the model and apply changes to the copy, only modifying the original model on commit. The thin option allows users to configure a ChangeApplier to apply transactional edits directly to the mode. This can be useful where performance is an issue, but should be used with care: With this option, rolling back changes instead of committing them is not possible.

boolean

false

Using Transactions

A transaction can be opened using the new initiate() function which returns a transaction object:

Code Block
javascript
javascript
var myApplier = fluid.makeChangeApplier(myModel);
var myTransaction = myApplier.initiate();
{code}

Once

...

the

...

transaction

...

has

...

been

...

create,

...

it

...

can

...

be

...

used

...

to

...

request

...

changes

...

to

...

the

...

model:

...

:}
Code Block
javascript
javascript
myTransaction.fireChangeRequest(requestSpec1);
myTransaction.fireChangeRequest(requestSpec2);
...
{code}

The

...

transaction

...

can

...

be

...

completed

...

using

...

the

...

new

...

commit()

...

function

...

of

...

the

...

transaction

...

object:

...

:}
Code Block
javascript
javascript
myTransaction.commit();
{code}

A

...

single

...

modelChanged

...

event

...

will

...

be

...

fired

...

on

...

completion

...

of

...

the

...

commit,

...

regardless

...

of

...

the

...

number

...

of

...

change

...

requests.

...

Post

...

Guards

...

Just

...

as

...

guard

...

listeners

...

are

...

notified

...

of

...

upcoming

...

changes

...

to

...

the

...

model

...

(offering

...

the

...

opportunity

...

to

...

prevent

...

those

...

changes),

...

post

...

guards

...

are

...

notified

...

of

...

an

...

upcoming

...

transactional

...

commit,

...

offering

...

the

...

opportunity

...

to

...

prevent

...

the

...

completion

...

of

...

the

...

entire

...

transaction.

...

Post

...

guards

...

are

...

NOT

...

notified

...

with

...

each

...

change

...

request,

...

but

...

only

...

once,

...

on

...

commit.

...

Post

...

guards

...

are

...

registered

...

similarly

...

to

...

regular

...

guards,

...

using

...

an

...

addListener

...

function:

...

:}
Code Block
javascript
javascript
myApplier.postGuards.addListener(pathSpec, guard, namespace);
{code}

Aside

...

from

...

when

...

post

...

guards

...

are

...

notified,

...

they

...

follow

...

the

...

same

...

specification,

...

function

...

signature,

...

etc.

...

as

...

fluid:regular

...

guards,

...

described

...

above

...

.

...

It

...

is

...

important

...

to

...

note

...

that

...

if

...

the

...

thin

...

options

...

is

...

true

...

,

...

then

...

transactional

...

changes

...

will

...

have

...

been

...

applied

...

to

...

the

...

model

...

directly,

...

and

...

any

...

post

...

guards

...

will

...

NOT

...

be

...

able

...

to

...

prevent

...

those

...

changes.

...

The

...

ChangeApplier

...

does

...

not

...

offer

...

the

...

ability

...

to

...

roll

...

back

...

changes

...

when

...

thin

...

is

...

true

...

(though

...

there's

...

nothing

...

stopping

...

users

...

from

...

attempting

...

to

...

undo

...

changes

...

themselves,

...

perhaps

...

within

...

a

...

post

...

guard).

...

Sneak Peek: Transactional Listeners

The ChangeApplier now supports transactional listeners. Note that this feature is in Sneak Peek status, and so its APIs will change. We encourage users to provide feedback by emailing our infusion-users mailing list.

Transactional listeners provide users the opportunity to attach listeners that will create a transaction when they are invoked, so that any changes which are requested by the listeners will occur seemingly as one operation with the initial change request.

Specifying Transactional Guard Listeners

To specify that a guard listener should be a transactional listener, users can use one of two techniques:

An exclamation mark preface on a pathSpec string

Code Block
javascript
javascript
myApplier.guards.addListener("!myModel.section.*", myGuardFunc);
{code}

*A {{transactional}} flag in a pathSpec object*

{code:javascript}

A transactional flag in a pathSpec object

Code Block
javascript
javascript
var pathSpec = {
    path: "myModel.section.*",
    transactional: true
};
myApplier.guards.addListener(pathSpec, myGuardFunc);
{code}