2.5.1 • Published 4 years ago

@gullerya/observable v2.5.1

Weekly downloads
-
License
MIT
Repository
github
Last release
4 years ago

GitHub npm npm Travis Codecov Codacy

Summary

Attention!!! I'll be removing this library in the near future since it is just anoter clone of the object-observer and exists only for the historical reasons, please move to the other one!

object-observer provides a deep observation of a changes performed on an object/array graph.

Main aspects:

  • implemented via native Proxy (revokable)
  • observation is 'deep', yielding changes from a sub-graphs too
  • changes delivered in a synchronous way
  • original objects are cloned while turned into Observables
  • arrays specifics:
    • generic object-like mutations supported
    • intrinsic Array mutation methods supported: pop, push, shift, unshift, reverse, sort, fill, splice
    • massive mutations delivered in a single callback, usually having an array of an atomic changes
  • intrinsic mutation methods of Map, WeakMap, Set, WeakSet (set, delete) etc are not observed (see this issue for more details)
  • following host objects (and their extensions) are not observed, but left as they are: Date, Blob, Number, String, Boolean, Error, Function, Promise, RegExp

Support matrix: CHROME61+ | FIREFOX60+ | EDGE16+ | NODE JS 8.10.0+

Performance report can be found here

Last versions (full changelog is here)

  • 2.5.0

    • fixed Issue no. 28 - fixing non-observable objects detection
  • 2.4.2

    • minor improvenent in the CI part of the library due to newer/better version of the test runner
  • 2.4.1

    • fixed Issue no. 27 - broken NodeJS dedicated distro
    • updated dependencies

For a short preview you may want to play with this JSFiddle.

Loading the Library

object-observer provided as an ES6 module. In NodeJS environment, that is not yet supporting ES6 modules (prior to v12.11.1 with experimental modules enabled), use the dedicated distribution as in example below.

//  browser
import { Observable } from 'dist/object-observer.min.js';

//  NodeJS (when NodeJS will fully support ES6 modules syntax - this one will be removed)
let Observable = require('./dist/node/object-observer').Observable;

API

Library implements Observable API as it is defined here.

Examples

Objects
let order = { type: 'book', pid: 102, ammount: 5, remark: 'remove me' },
    observableOrder = Observable.from(order);

observableOrder.observe(changes => {
    changes.forEach(change => {
        console.log(change);
    });
});


observableOrder.ammount = 7;
//  { type: 'update', path: ['ammount'], value: 7, oldValue: 5, object: observableOrder }


observableOrder.address = {
    street: 'Str 75',
    apt: 29
};
//  { type: "insert", path: ['address'], value: { ... }, object: observableOrder }


observableOrder.address.apt = 30;
//  { type: "update", path: ['address','apt'], value: 30, oldValue: 29, object: observableOrder.address }


delete observableOrder.remark;
//  { type: "delete", path: ['remark'], oldValue: 'remove me', object: observableOrder }
Arrays
let a = [ 1, 2, 3, 4, 5 ],
    observableA = Observable.from(a);

observableA.observe(changes => {
    changes.forEach(change => {
        console.log(change);
    });
});

//  observableA = [ 1, 2, 3, 4, 5 ]
observableA.pop();
//  { type: 'delete', path: [4], oldValue: 5, object: observableA }


//  now observableA = [ 1, 2, 3, 4 ]
//  following operation will cause a single callback to the observer with an array of 2 changes in it)
observableA.push('a', 'b');
//  { type: 'insert', path: [4], value: 'a', object: observableA }
//  { type: 'insert', path: [5], value: 'b', object: observableA }


//  now observableA = [1, 2, 3, 4, 'a', 'b']
observableA.shift();
//  { type: 'delete', path: [0], oldValue: 1, object: observableA }


//  now observableA = [ 2, 3, 4, 'a', 'b' ]
//  following operation will cause a single callback to the observer with an array of 2 changes in it)
observableA.unshift('x', 'y');
//  { type: 'insert', path: [0], value: 'x', object: observableA }
//  { type: 'insert', path: [1], value: 'y', object: observableA }


//  now observableA = [ 2, 3, 4, 'a', 'b' ]
observableA.reverse();
//  { type: 'reverse', path: [], object: observableA } (see below and exampe of this event for nested array)


//  now observableA = [ 'b', 'a', 4, 3, 2 ]
observableA.sort();
//  { type: 'shuffle', path: [], object: observableA } (see below and exampe of this event for nested array)


//  observableA = [ 2, 3, 4, 'a', 'b' ]
observableA.fill(0, 0, 1);
//  { type: 'update', path: [0], value: 0, oldValue: 2, object: observableA }


//  observableA = [ 0, 3, 4, 'a', 'b' ]
//  the following operation will cause a single callback to the observer with an array of 2 changes in it)
observableA.splice(0, 1, 'x', 'y');
//  { type: 'update', path: [0], value: 'x', oldValue: 0, object: observableA }
//  { type: 'insert', path: [1], value: 'y', object: observableA }


let customer = { orders: [ ... ] },
    oCustomer = Observable.from(customer);

//  sorting the orders array, pay attention to the path in the event
oCustomer.orders.sort();
//  { type: 'shuffle', path: ['orders'], object: oCustomer.orders }


oCustomer.orders.reverse();
//  { type: 'reverse', path: ['orders'], object: oCustomer.orders }

Arrays notes: Some of array operations are effectively moving/reindexing the whole array (shift, unshift, splice, reverse, sort). In cases of massive changes touching presumably the whole array I took a pessimistic approach with a special non-detailed events: 'reverse' for reverse, 'shuffle' for sort. The rest of these methods I'm handling in an optimistic way delivering the changes that are directly related to the method invocation, while leaving out the implicit outcomes like reindexing of the rest of the Array.

Observation options
let user = {
	    firstName: 'Aya',
	    lastName: 'Guller',
	    address: {
	    	city: 'of mountaineers',
	    	street: 'of the top ridges',
	    	block: 123
	    }
    },
    oUser = Observable.from(user);

//  going to observe ONLY the changes of 'firstName'
oUser.observe(changes => {}, {path: 'firstName'});

//  going to observe the changes from 'address' and deeper
oUser.observe(changes => {}, {pathsFrom: 'address'});
2.5.1

4 years ago

2.5.0

4 years ago

2.4.2

5 years ago

2.4.1

5 years ago

2.4.0

5 years ago

2.3.0

5 years ago

2.2.0

5 years ago

2.1.0

5 years ago

2.0.2

5 years ago

2.0.1

5 years ago

2.0.0

5 years ago

1.2.0

5 years ago

1.1.5

5 years ago

1.1.4

6 years ago

1.1.3

6 years ago

1.1.2

6 years ago

1.1.1

6 years ago

1.1.0

6 years ago

1.0.6

6 years ago

1.0.5

6 years ago