decor/Observable
Observable
is an object working as a shim of ES7 Object.observe()
.
Observable
has .set()
method for automatic emission of change record, and static Observable.observe()
method to observe for that, for example:
Similar to ES7 Object.observe()
,
change records are delivered in a batch at the end of the micro-task.
In above example, you'll see that change to foo
property and change to bar
properties are notified in a single callback, in a format of array.
The format of change records is compatible with ES7 Object.observe()
.
Under the hood, Observable.observe()
directly uses ES7 Object.observe()
if it's available natively in browser.
Synchronous delivery of change record
There are some cases you want to deliver change records immediately,
instead of waiting for the end of micro-task.
For that purpose, you can use Observable.deliverChangeRecord()
that synchronously delivers change records that are queued for callback.
Here's an example:
Manual emission of change record
Similar to Object.observe()
, .set()
won't automatically emit a change record if:
- There is no actual change in value
- The given property has a setter (ECMAScript setter)
In such conditions, you can manually emit a change record (and queue it for delivery) by Observable.getNotifier(observable).notify(changeRecord)
method, which is what .set()
calls under the hood. Here's an example:
Synthetic change record
If you make a higher-level change to an object (as opposed to a simple property update/delete/add), such as array splice, having such higher-level change represented directly in a change record will be useful. You can do that by Observable.getNotifier(observable).performChange(type, callback)
method. The first argument (type
) is the change type you define for your higher-level change. The second argument is a function where you can make a series of changes that your higher-level change represents, and then return your higher-level change.
You can observe your higher-level change by adding the change type of your higher-level change to the third argument of Observable.observe()
. Otherwise, raw changes to Observable
(update
, etc.) will be observed.
Here's an example, where Point#move()
method moves the point with the given distance and direction (angle
). The code to update x
and y
with the result is in performChange()
callback so that the updates are represented by a change record with move
type, with the distance and direction (angle
):
Utility functions
Observable
has two static utility funcitons:
Observable.is(value0, value1)
- Comparesvalue0
andvalue1
. Basically returnsvalue0 === value1
, except:- Returns
true
forNaN
andNaN
- Returns
false
for+0
and-0
- Returns
Observable.assign(observable, object0, object1...)
- Copies all enumerable properties inobject0
,object1
, ... toobservable
, and automatically emits change records for those properties.
ObservableArray
ObservableArray
is an object that extends native JavaScript array, and works as a shim of ES7 Array.observe()
. Like Observable
, ObserableArray
has .set()
method for automatic emission of change record, and static ObservableArray.observe()
method to observe for that. Here's an example:
In addition to .set()
, the following methods automatically emit change records, too:
pop()
push()
shift()
unshift()
splice()
reverse()
sort()
Here's an example with .splice()
:
Under the hood, ObservableArray.observe()
uses ES7 Array.observe()
if it's available natively in browser.
Change record translation and ES7 Array.observe()
compatibility
With ObservableArray.observe()
, change records are translated to a synthetic version representing array splice where applicable. It makes ObservableArray.observe()
compatible to ES7 Array.observe()
except the following conditions:
- Any of splices are adjacent or intersect;
ObservableArray.observe()
attemps to merge them. If makes what liaison or your applicatoin do with the callback, e.g. DOM updates, more efficient. - Result of
ObserableArray#reverse()
;Array.observe()
emits "update" type of change records for array entries. - Result of
ObserableArray#sort()
;Array.observe()
emits "update" type of change records for array entries.
Synchronous delivery of change record
There are some cases you want to deliver change records immediately, instead of waiting for end of micro-task. For that purpose, you can use .deliver()
method of the return value of ObservableArray.observe()
.
Here's an example:
This is different from Observable.deliverChangeRecords()
explained in Observable
section; As explained above, ObservableArray.observe()
attemps to merge change records of splice if any of them are adjacent or intersect.
To do that, ObservableArray.observe()
calls Array.observe()
or Observable.observe()
(depending on whether Array.observe()
is availble native in browser) with its own callback, instead of the callback given to ObservableArray.observe()
.
Explicit way of emitting change records
liaison takes an approach where you make an explicit API call to emit change records of an object or an array for observation. With this approach, the underlying system doesn't need to do a heavylifting to compare old/new values of all object properties observed in your application, which becomes non-trivial for bigger-scale applications.
For example,
observable.set("foo", "FooValue1")
explicitly emits a change record of observable
object for observation. If it were observable.foo = "FooValue1"
change record would not be emitted and thus observation callback would't be called (unless ES7 Object.observe()
is availble in your browser).
Similar for value assignment to array by index, call
observableArray.set(n, "ValueForObservableArrayN")
to explicitly emit change records of observableArray
for observation. If it were observableArray[n] = "ValueForObservableArrayN"
change records would not be emitted and thus observation callback won't be called (unless ES7 Array.observe()
is availble in your browser).
Here is the list of the APIs you can use to explicitly emit change records:
Observable
(In decor library)#set()
.assign()
(New in 0.6.0)
ObservableArray
#set()
#pop()
#push()
#shift()
#unshift()
#splice()
#reverse()
#sort()