Giter Club home page Giter Club logo

object-observer's Introduction

npm GitHub

Quality pipeline Codecov Codacy

object-observer

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

Main aspects and features:

  • implemented via native Proxy (revokable)
  • observation is 'deep', yielding changes from a sub-graphs too
  • nested objects of the observable graph are observables too
  • changes delivered in a synchronous way by default, asynchronous delivery is optionally available as per Observable configuration; more details here
  • observed path may optionally be filtered as per observer configuration; more details here
  • original objects are cloned while turned into Observables
    • circular references are nullified in the clone
  • array specifics:
    • generic object-like mutations supported
    • intrinsic Array mutation methods supported: pop, push, shift, unshift, reverse, sort, fill, splice, copyWithin
    • massive mutations delivered in a single callback, usually having an array of an atomic changes
  • typed array specifics:
    • generic object-like mutations supported
    • intrinsic TypedArray mutation methods supported: reverse, sort, fill, set, copyWithin
    • 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 skipped from cloning / turning into observables: Date

Supported: CHROME71+ | FIREFOX65+ | EDGE79+ | SAFARI12.1 | NODE JS 12.0.0+

Performance report can be found here.

Changelog is here.

Preview

For a preview/playground you are welcome to:

  • CodePen - Observable.from() flavor
  • CodePen - new ObjectObserver() flavor

Install

Use regular npm install @gullerya/object-observer --save-prod to use the library from your local environment.

ES module:

import { Observable } from '@gullerya/object-observer';

CJS flavor:

const { Observable } = require('@gullerya/object-observer');

Huge thanks to seidelmartin providing the CJS build while greatly improving the build code overall along the way!

CDN (most suggested, when possible):

import { Observable } from 'https://libs.gullerya.com/object-observer/x.y.z/object-observer.min.js';

Replace the x.y.z with the desired version, one of the listed in the changelog.

CDN features:

  • security:
    • HTTPS only
    • intergrity checksums for SRI
  • performance
    • highly available (with many geo spread edges)
    • agressive caching setup

Full details about CDN usage and example are found here.

API

Library implements Observable API as it is defined here.

There is also a 'DOM-like' API flavor - constructable ObjectObserver. This API is resonating with DOM's MutationObserver, ResizeObserver etc from the syntax perspective. Under the hood it uses the same Observable mechanics. Read docs about this API flavor here.

object-observer is cross-instance operable. Observables created by different instances of the library will still be detected correctly as such and handled correctly by any of the instances.

Security

Security policy is described here. If/when any concern raised, please follow the process.

Examples

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

Observable.observe(observableOrder, 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 }

Object.assign(observableOrder, { amount: 1, remark: 'less is more' }, { async: true });
//  - by default the changes below would be delivered in a separate callback
//  - due to async use, they are delivered as a batch in a single callback
//  { type: 'update', path: ['ammount'], value: 1, oldValue: 7, object: observableOrder }
//  { type: 'insert', path: ['remark'], value: 'less is more', object: observableOrder }
Arrays
let a = [ 1, 2, 3, 4, 5 ],
    observableA = Observable.from(a);

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


//  observableA = [ 1, 2, 3, 4, 5 ]
observableA.pop();
//  { type: 'delete', path: [4], value: undefined, 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', oldValue: undefined, object: observableA }
//  { type: 'insert', path: [5], value: 'b', oldValue: undefined, object: observableA }


//  now observableA = [1, 2, 3, 4, 'a', 'b']
observableA.shift();
//  { type: 'delete', path: [0] value: undefined, 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', oldValue: undefined, object: observableA }
//  { type: 'insert', path: [1], value: 'y', oldValue: undefined, 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', oldValue: undefined, 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

object-observer allows to filter the events delivered to each callback/listener by an optional configuration object passed to the observe API.

In the examples below assume that callback = changes => {...}.

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

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

//  going to observe ONLY the changes of 'address.city'
Observable.observe(oUser, callback, {path: 'address.city'});

//  pathsOf
//
//  going to observe the changes of 'address' own properties ('city', 'block') but not else
Observable.observe(oUser, callback, {pathsOf: 'address'});
//  here we'll be notified on changes of
//    address.city
//    address.extra

//  pathsFrom
//
//  going to observe the changes from 'address' and deeper
Observable.observe(oUser, callback, {pathsFrom: 'address'});
//  here we'll be notified on changes of
//    address
//    address.city
//    address.extra
//    address.extra.data

object-observer's People

Contributors

gullerya avatar hpdemo avatar jloscombe avatar seidelmartin avatar tonis2 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

object-observer's Issues

chore: CDN deployment

I'd like to consider to publish the fully workable version of the lib to CDN.
Add it to the documentation for an easy consumption and trials.

ReferenceError: Blob is not defined

I am using this module most of my time on the server side, i guess there was an update to this module and i started to getting new errors, here is one of them:

} else if (target instanceof Date || target instanceof Blob || target instanceof Error) {
		                                                       ^

ReferenceError: Blob is not defined

The blob class is not part of the nodejs so the module throwing the error.
To fix it i installed cross-blob package to make it work in nodejs. I hope it will help to someone!

Shuffle/Reverse events don't come with paths

When a nested array is sort()ed or reverse()d, the library generates a shuffle or reverse event, respectively. These events do not include a path and it is therefore not possible to distinguish which internal array has been modified. For example:

> const Observe = require('object-observer');
> const target = Observe.from({foo: [1, 3, 2], bar: [7, 9, 8]});
> target.observe(c => { console.log(c); });
> target.foo.sort();
[ { type: 'shuffle' } ]

Ideally these events would come with a path field, just like other sorts of events:

> target.foo.sort();
[ { type: 'shuffle', path: ['foo'] } ]

Using Symbols for isObservable

Hi!

I've been through the code in order to use this as a dependency for one of my vanilla JS project and I've seen that the method Observable.isObservable uses some typeof checks to verify that the object is observable.

I think that it might be better to use Symbols like the Array.isArray method. To be more precise, there is an article that goes deep on the advantages to use Symbols and how they are used in javascript is methods here.

Batching changes made via Object.assign?

Thank you for this excellent library.

My scenario is that I'm using Object.assign, which is (correctly) invoking the callback for each implicit assignment. So if I have an object with two properties, the callback is invoked twice.

However, from the application standpoint, this is one change. Is there some way to detect this? Or group the changes? The argument to the callback is an array, but always appears to have just one value.

I'm guessing (I have not studied the code) that this is just a set trap which then invokes the callback. I don't have any idea how you might group changes together. But I thought perhaps this might be something to which you've given some thought. Is there a way to do this? If not, is there a good workaround?

ES6 Map. WeakMap, Set, WeakSet support

Hi,

great library there, after making some tests, all is working great, except no support for the for mentioned new ES6 Data Types, is there a possibility to support them?

thanks in advanced.

"TypeError: Cannot perform 'get' on a proxy that has been revoked thrown" in Object.assign

Problem: TypeError: Cannot perform 'get' on a proxy that has been revoked thrown in Object.assign.

minimal code:

var oldData = { b : {b1 : "x", b2 : "y"  } };
var newData = { b : { b1: "z" } };

var observableData = require("object-observer").Observable.from(oldData);
observableData.observe(dataObserver);
Object.assign(observableData, newData);

function dataObserver(changes) {
  changes.forEach(change => {
    console.log(change.type + " of " + change.path + " from " + change.value + " to " + change.oldValue);
  });
};

Proposal:
I'm javascript newbie, so no proposals. Error might be on my side.

Relevant version/s (if any): 0.2.4

(node v8.9.4)

remove CJS fashioned distro for older NodeJS

Since NodeJS 14 is already in ACTIVE mode and soon will be going to MAINTENANCE, both of which are a suggested modes for production use, and since NodeJS 14 provides ES6 modules supports OOTB, I shall remove the special distro and all related build steps for the older CommonJS 'require' syntax.

How To get the changed object ?

this is not a issue. But not sure where to ask so putting it up here.

Based on the example:

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

observableOrder.ammount = 7;

I need to submit observableOrder to the backend.
observableOrder should be
{ type: 'book', pid: 102, ammount: 7, remark: 'remove me' }

But when I
console.log(observableOrder);
I get the following

Proxy {amount: 7, pid: 102, type: "book", address: Proxy, revoke: ƒ, …}
[[Handler]]: ObjectObserver
isRevoked: false
proxy: Proxy {amount: 7, pid: 102, type: "book", address: Proxy, revoke: ƒ, …}
revokable: {proxy: Proxy, revoke: ƒ}
target: {amount: 7, pid: 102, type: "book", address: Proxy, revoke: ƒ, …}
observers: Map(1) {ƒ => {…}}
__proto__: ObserverBase
[[Target]]: Object
address: Proxy {apt: 30, street: "Str 75", Symbol(system-observer-key): ObjectObserver}
amount: 7
pid: 102
type: "book"
Symbol(system-observer-key): ObjectObserver {isRevoked: false, revokable: {…}, proxy: Proxy, target: {…}, observers: Map(1)}
observe: ƒ value(observer, options)
revoke: ƒ value()
unobserve: ƒ value()
__proto__: Object
[[IsRevoked]]: false

How Can I get the original value back without the attached properties?

Prototype chain preservation

Hi,

I'm using your library with minor tweak. I've added support for prototype chain preservation.

Solution is simple. Add target.__proto__ = source.__proto__; on line 100.

support for calculated properties?

It may be useful to be able to 'observe' calculated fields. For example:

let user = {
    firstName: 'Some',
    lastName: 'Name',
    get fullName() {
        return this.firstName + '  ' + this.lastName;
    }
};

It is obviously possible to observe the firstName and the lastName. But it would be cool to tell ObjectObserver, that fullName is dependent on those two and each time one of them being changed - check the newly calculated value of the fullName and if changed - dispatch event for the fullName change as well.

Things that are still not clear to me:

  • should the calculated property be only of getter implementation or function as well
  • should calculated properties be updated and notifies on each dependency update or should there be some API of 'atomic unit of change' (second option seems more appropriate, but complicates things a lot, including consumption part)

Object references

if you do this with standard object:
var t={a:{},b:{}}
undefined
t.a.c='i'
"i"
t.b=t.a
{c: "i"}
t.a.c
"i"
t.b.c
"i"
t.a.c='o'
"o"
t.a.c
"o"
t.b.c
"o"

with your object observer you will see that t.b.c. will be "i".
Because when you do t.b=t.a you clone t.a.
I think in this case we don't need to clone but to add another parent to the observer.
It will complicate a lot the code but it will be as close as possible to a standard js object
Thank you.
ps I think your work is huge
ps2 revoke oldvalue after deliver the changes because if newvalue is a child of oldvalue it will throw an error

provide a changed object in change event data

it might be useful to provide an immediate object that the change was performed on
thus, even when the path of the change is a path from the base Observable, the last node in the path will be the property / index on the provided object - an immediate subject of the change in a nested observable graph

consider adding the object property to the change event, similar to the one in the Object.observe original spec (nowadays - deprecated)

DOM-like ObjectObserver API flavor

As of now Observable API behaves as following:

  • create Observable from what to observe
  • add to that Observable a logic of how (callback, the actual logic) further

I'd like to explore a possibility to add another, more DOM-like flavor to the API:

  • create ObjectObserver with the callback - the how once
  • with that one set the observed subjects (hence what) further

The second API flavor is more resonating with the APIs like MutationObserver and ResizeObserver.
It would also be a good extensibility exercise for the library to see if it's capable of such an enhancements.

replace default export with explicitly named export of an API

Influenced by this article, I'd like to move away from default export of an API to the explicitly named one.

In ES6 flavor of library usage today, one may do the following:
import Observable from '../dist/module/object-observer.js'
This is quite good, I'd say.

But others would follow some other convention of naming objects after the file name and would do:
import ObjectObserver from '../dist/module/object-observer.js'`
This is quite a miss from APIs visibility and clarity point of view.

Option to disallow adding new and removing existing propertis to the observable

It would be nice to have an option where the observable object is prevented from new properties being added , and existing properties being removed (similar behavior to Object.seal() )

This will serve the use case where we only want to change/mutate the existing properties (e.g. application state managements, etc.).

Thank you!

nested observable should behave/detach correctly

nested observable should

  • provide relative (partial) path when observed on their own
  • should not trigger ancestors' callback when detached
  • should trigger correctly new ancestors' callback when re-attached

Warning: require() of ES modules is not supported.

Hello,
For some reason i am starting getting this error, this module used to work fine but now i am not able to work with it... Is there a fix?

let observ = require('./object-observer/dist/node/object-observer');
Warning: require() of ES modules is not supported.
require() of C:\Users\user\Desktop\avrorajs\bin\object-observer\dist\node\object-observer.js from C:\Users\user\Desktop\project\bin\bundle.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename object-observer.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from C:\Users\user\Desktop\project\bin\object-observer\package.json.

add support to copyWithin arrays method

Add support to copyWithin array's method.
As of now, it seems to me that this functionality should be treated same as fill or splice from the detailing perspective.

Method to access the original underlying object?

Hello,

I was wondering if there was an easy way to access the the original object from just the proxy. I'm looking for the actual object reference, not just a copy.

If not, would it be possible to add a function that would work as follows:

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

order === originalOrder // true

chore: improve automation flow of version bump post release

As of now the script that is releasing the version extracts the version from the tag.
Chore proposals:

  • on the master there will already be set a next version to be released, suffixed with something like '-snapshot'
  • release will actually release the very version that is present in master
  • then bump the master to the next snapshot (let's say next patch)
  • during the development of the next version, it is possible to manually bump the snapshot further, if needed

Error for load library and "Unexpected token export"

The call of the files is not working properly.
let Observable = require('./dist/node/object-observer').Observable;

I had to replace it with
let Observable = require('./node_modules/object-observer/dist/node/object-observer').Observable;

And now that I managed to load the library I have an error

/node_modules/object-observer/dist/node/object-observer.js:632
export { Observable };
^^^^^^

SyntaxError: Unexpected token export
    at new Script (vm.js:79:7)
    at createScript (vm.js:251:10)
    at Object.runInThisContext (vm.js:303:10)
    at Module._compile (internal/modules/cjs/loader.js:657:28)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:22:18)

Add "get" trap

Hi, great work on the library! I'm currently using it for another project but would really love the ability to enable a "get" trap for any deep getters triggered.

I imagine this would probably be best done as an option to .observe and turned off by default, as most people would be interested in setters. However by adding this trap you have even more ability with this library to observe deep object usage.

Would you consider adding this? I was considering forking it but of course being the author you would get this done faster and without chance of regression or bugs.

I also use TypeScript and have a definition file I made by hand.

declare module 'object-observer' {
   export interface Change {
      type: 'insert' | 'update' | 'delete' | 'shuffle' | 'reverse';
      path: Array<string | number>;
      value: any;
      oldValue: any;
      object: any;
   }

   export type ChangesHandler = (changes: Change[]) => void;

   export interface Options {
      path?: string;
      pathsOf?: string;
      pathsFrom?: string'
   }

   export class Observable {
      static from(obj: any): Observable;
      static isObservable(obj: any): boolean;
      observe(callback: ChangesHandler, options?: Options): void;
      unobserve(): void;
   }
}

Feel free to modify it, or submit it to @types/object-observer.

Thanks again!
Ali

fix value in the callback when new value is a child object of an old value

When objects being replaced on the observed graph, old object and all of its subgraph is being revoked and new object becomes cloned and observable.

In case when new value is child of an old value, in the callback the new value appears to be non-usable revoked proxy. See flow below.
This affects callbacks only, the values on the observed graph itself are okay.

let g = {
    a: {
        b: {
            prop: 'text'
        },
        prop: 'text'
    }
};
let og = Observable.from(g);
og.observe(changes => {
    changes[0].value.prop === 'text';        // at this point an error is thrown
});

og.a = og.a.b;

og.a.prop === 'text';            // this is fine, only the value within the callback is affected

Observe specific path

Some additions to API are needed to observe a specific object path.

In example, I've an object like:

{
 a: {
  b: {
   c: 'value'
  }
 }
}

I would like to subscribe only to "a.b.c." updates (even if it doesn't exist yet), not to any update in object.

performance tests - set actually expected thresholds

As of now, the performance benchmarks are running for the console, the results are not really asserted.
I should have some reasonable thresholds set and assured as to be able to clearly see the degrade if any.
Those thresholds will also become a performance waterline mark.

changing value to equal one causes change event to be fired

As of now, setting of observable object property to an even strictly equal value causes a change event to be fired:

let o = Observable.from({ p: true });

o.p = true;        // this will fire an update event

object-observer should NOT fire events when strictly equal primitives are involved in the change

changes not being observed,

Hi
Thank you for this package - I'm hoping it's going to solve my problem but I'm unable to observe a change.

Initially I set up an observer within a class to another class - I want to look for an update to a property and then take action. Changes to the property were made but not change logged. Does this code recognise changes within a class syntax?

I then decided to create an external object that is updated from the class I was observing and observe that instead but again nothing noted when the values are updated.

I'm doing something wrong

my bare bones code:

import { Observable } from 'object-observer/dist/object-observer.js';
import {selectedAsset} from "./filemanager-ha

export let fileImageWidgetControls = class {

openFileManager = () => {

        const filemanager = new filemanagerHandler({
            images:true
        });
        filemanager.init();
        let filemanagerObserver = Observable.from(selectedAsset);
        filemanagerObserver.observe(changes => {
            changes.forEach(change => {
                console.log("filemanager change:", change);
            });
        });
}

export let filemanagerHandler = class {
   handleSelection = (element, type, callback) => {

        selectedAsset = {
            filename: element.attr('href'),
            id: element.data('id'),
            caption: element.data('caption'),
            type: type
        }
        console.log("selected:", selectedAsset);

    }
}
export let selectedAsset = {

}

so the first class interacts with the second class that after some user action updates selectedAsset but no changes notified

console logging suggests selectedAsset is being updated as expected

Any help appreciated

revokation - reconsideration

As of now, objects removed from an observed graph are being revoked.
It is not something that we MUST have, actually I've opted it in just as a hardening API feature, not much of thought was invested here.

Now, when subgraphs may function on their own as an observables, it is actually a breaking thing, since some consumer may get a reference to the subgraph and observe or just use it, and enocounter erroneous situation is somewhere upper the chain the obect was detached from the observable graph.

Suggestion - revokation should be OFF by default (leave some feasable option to opt it in once this feature will be required).

Please create a jsfiddle that shows how to use it

Makes it easier.. I guess this is just a suggestion, but trying out if something works for me is a lot of work when you look for libraries.

I tried hooking your library up to some dom elements somehow, not sure how to do it:

http://jsfiddle.net/djthkd6L/1

for example in your sample:

var order = { type: 'book', pid: 102, ammount: 5, remark: 'remove me' },
	observableOrder;
observableOrder = Observable.from(order);
observableOrder.observe(changes => {
	changes.forEach(change => {
		console.log(change);
	});
});
observableOrder.ammount = 7;		// { type: 'update', path: ['ammount'], value: 7, oldValue: 5 }
observableOrder.address = {			// { type: "insert", path: ['address'], value: { ... } }
	street: 'Str 75',
	apt: 29
};
observableOrder.address.apt = 30;	// { type: "update", path: ['address','apt'], value: 30, oldValue: 29 }
delete observableOrder.remark;		// { type: "delete", path: ['remark'], oldValue: 'remove me' }

this wont actually update the order object... and the change events dont even tell me which object was the original object, which I assume I'd have to update myself during the callback? Is that correct?
still leaves open how to make all this work with html elements

observed array spice with insert do not indexes object correctly

The following behavior is malfunctioning:

let a = [{text: 'a'}, {text: 'b'}, {text: 'c'}, {text: 'd'}];
let pa = Observable.from(a);
...
pa.splice(1, 2, {text: '1'}, {text: '2'}, {text: '3'});
// now array will looks like this: [{text: 'a'}, {text: '1'}, {text: '2'}, {text: '3'}, {text: 'd'}]

From this point on, changes on the last item ({text: 'd'}) should deliver the changes with path 4.
But the changes are delivered with path 3.
Last item's path should be reindexed correctly according to the newly pushed items (array expansion).

add support for a TypedArray

As of now, TypedArrays are not treated as Arrays actually, but as a regular object.
This would eventually break the logic at some point, since when these ones are getting pre-processed during the observification process, they are turned to a regular objects and loosing their special TypedArray methods.

mid (entry) point - observe from the middle

As of now, if by any chance a consumer gets a nested object from an Observable path, it is not possible to just start observing that object.
In fact it is even not possible to know that it is part of on already Observable graph.

Proposal: make it possible to observe the nested parts of the Observable just as it would be a root. It should be quite an easy, since internally object-observer may understand that the object is part of an Observable and just set the observe callbacks to react on the paths from that point and below.

Issues:

  • this will actually require to override/lock observe, unobserve properties not only on the Observable root, but on any nested object as well
  • revokation - should be left on the root? presumably yes, but since we seemingly looking for a transparent knowledge of the nested Observable so the revokation (inability of that) should also be transparent (some message to console)?

allow to observe root path (only)

There is a use case, when one would like to observe the changes on the root level only.

It's not possible today, since the API rejects an empty string in the { path: '<path>' } option.

Should be allowed.

Consider adding array of paths to observer options

This lib is great!

Could you add the ability to do the following:

let user = {
        firstName: 'Aya',
        lastName: 'Guller',
       phone: {
           number: '023887942'
        },
        address: {
            city: 'of mountaineers',
            street: 'of the top ridges',
            block: 123,
            extra: {
                data: {}
            }
        }
    },
    oUser = Observable.from(user);
 
oUser.observe(callback, {paths: [ 'firstName', 'address' ]});
oUser.observe(callback, {pathsOf: [ 'phone', 'address.extra' ]});
oUser.observe(callback, {pathsFrom: [ 'phone', 'address' ]});

They'd function the same way as their counterparts but only that we are observing multiple paths.

remove non-ES6 flavor

Non ES6 flavor of library consumption is still here, as you can see from the readme docs.
Active support and development of the features in this mode is already mostly not happening, only defect fixes.
As the community runs fast toward ES6 scripting and import/export are everywhere and supported by a vast majority of build/transpile tools, I'd like to remove the support of the non-ES6 flavor (the one that you should do the reference to the script from HTML) whatsoever.

Attention! ES6 like imports will be slightly impacted as well - I'll bring them from being under ../dist/module folder to by under ../dist folder directly.

Fiddle not working?

I would like to try this library, but I can't get past loading. Even the fiddle linked doesn't work at all ("import a reserved word"). I have tried many permutations of require, import, etc, but am getting errors about modules.
Is there a working fiddle for this?

do not attempt to revoke proxies on non-observables

When observing object having non-observable property, and this property is being removed/replaced - ObjectObserver should not attempt to revoke a proxy of an old value, it's simply failing on undefined since the proxy is not exists.
Reproduce:

let o = {
   test: Promise.resolve()
},
oo = Observable.from(o);

// for this flow we must have a callbacks wired to the observable
oo.observe(function() {});

oo.test = null;   // this line will throw exception

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.