All Projects → ElliotNB → Observable Slim

ElliotNB / Observable Slim

Licence: mit
Observable Slim is a singleton that utilizes ES6 Proxies to observe changes made to an object and any nested children of that object. It is intended to assist with state management and one-way data binding.

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Observable Slim

Observer Util
Transparent reactivity with 100% language coverage. Made with ❤️ and ES6 Proxies.
Stars: ✭ 905 (+408.43%)
Mutual labels:  observable, proxy, data-binding
mutable
State containers with dirty checking and more
Stars: ✭ 32 (-82.02%)
Mutual labels:  state-management, observable
NestedReact
BackboneJS compatibility layer for React-MVx MVVM framework.
Stars: ✭ 79 (-55.62%)
Mutual labels:  state-management, data-binding
Radioactive State
☢ Make Your React App Truly Reactive!
Stars: ✭ 273 (+53.37%)
Mutual labels:  proxy, state-management
reactive state
An easy to understand reactive state management solution for Flutter.
Stars: ✭ 19 (-89.33%)
Mutual labels:  state-management, observable
mst-effect
💫 Designed to be used with MobX-State-Tree to create asynchronous actions using RxJS.
Stars: ✭ 19 (-89.33%)
Mutual labels:  state-management, observable
micro-observables
A simple Observable library that can be used for easy state management in React applications.
Stars: ✭ 78 (-56.18%)
Mutual labels:  state-management, observable
vana
Observe your immutable state trees 🌲👀 (great with React)
Stars: ✭ 24 (-86.52%)
Mutual labels:  state-management, observable
Dob
Light and fast 🚀 state management tool using proxy.
Stars: ✭ 713 (+300.56%)
Mutual labels:  observable, proxy
Mobx State Tree
Full-featured reactive state management without the boilerplate
Stars: ✭ 6,317 (+3448.88%)
Mutual labels:  observable, state-management
Icaro
Smart and efficient javascript object observer, ideal for batching DOM updates (~1kb)
Stars: ✭ 568 (+219.1%)
Mutual labels:  observable, proxy
Utility
Assign/Partial/ReadOnly/Proxy
Stars: ✭ 31 (-82.58%)
Mutual labels:  observable, proxy
Concent
State management that tailored for react, it is simple, predictable, progressive and efficient.
Stars: ✭ 882 (+395.51%)
Mutual labels:  proxy, state-management
Mag.js
MagJS - Modular Application Glue
Stars: ✭ 157 (-11.8%)
Mutual labels:  observable, state-management
Shadowsocks Rust
Oh my implementation of Shadowsocks in Rust
Stars: ✭ 171 (-3.93%)
Mutual labels:  proxy
Smtpd
A Lightweight High Performance ESMTP email server
Stars: ✭ 175 (-1.69%)
Mutual labels:  proxy
Brook
Brook is a cross-platform strong encryption and not detectable proxy. Zero-Configuration. Brook 是一个跨平台的强加密无特征的代理软件. 零配置.
Stars: ✭ 12,694 (+7031.46%)
Mutual labels:  proxy
Proxy pool
Python爬虫代理IP池(proxy pool)
Stars: ✭ 13,964 (+7744.94%)
Mutual labels:  proxy
Dnsproxy
防 DNS 缓存污染,兼顾查询质量与速度
Stars: ✭ 177 (-0.56%)
Mutual labels:  proxy
Pydesignpattern
Design Pattern that described by Python, This is the source code for the book of Everybody Know Design Patterns.
Stars: ✭ 174 (-2.25%)
Mutual labels:  proxy

👀 Observable Slim

Build Status Coverage Status Monthly Downloads

https://github.com/elliotnb/observable-slim

Version 0.1.5

Licensed under the MIT license:

http://www.opensource.org/licenses/MIT

Overview

Observable Slim is a singleton that utilizes ES6 Proxies to observe changes made to an object and any nested children of that object. Observable Slim aspires to be as highly performant and lightweight as possible. Minifies down to 5KB.

Observable Slim was originally built as part of the Nimbly JS framework where it assisted with state management, state mutation triggers and one-way data binding. Observerable Slim was separated out from Nimbly in order to service other use cases outside of the scope of the Nimbly framework.

Install

<script src="observable-slim.js"></script>

Also available via NPM:

$ npm install observable-slim --save

Usage

Create an observer

The create method is the starting point for using Observable Slim. It is invoked to create a new ES6 Proxy whose changes we can observe. The create method accepts three parameters:

  1. target - Object, required, plain JavaScript object that we want to observe for changes.
  2. domDelay - Boolean, required, if true, then Observable Slim will batch up observed changes to target on a 10ms delay (via setTimeout). If false, then observer will be immediately invoked after each individual change made to target. It is helpful to set domDelay to true when your observer function makes DOM manipulations (fewer DOM redraws means better performance).
  3. observer - Function, optional, will be invoked when a change is made to the proxy of target. When invoked, the observer function is passed a single argument -- an array detailing each change that has been made (see below).

The create method will return a standard ES6 Proxy.

var test = {};
var p = ObservableSlim.create(test, true, function(changes) {
	console.log(JSON.stringify(changes));
});

p.hello = "world";   
// Console log:
// [{"type":"add","target":{"hello":"world"},"property":"hello","newValue":"world","currentPath":"hello","jsonPointer":"/hello","proxy":{"hello":"world"}}]

p.hello = "WORLD";
// Console log:
// [{"type":"update","target":{"hello":"WORLD"},"property":"hello","newValue":"WORLD","previousValue":"world","currentPath":"hello","jsonPointer":"/hello","proxy":{"hello":"WORLD"}}]

p.testing = {};   
// Console log:
// [{"type":"add","target":{"hello":"WORLD","testing":{}},"property":"testing","newValue":{},"currentPath":"testing","jsonPointer":"/testing","proxy":{"hello":"WORLD","testing":{}}}]

p.testing.blah = 42;   
// Console log:
// [{"type":"add","target":{"blah":42},"property":"blah","newValue":42,"currentPath":"testing.blah","jsonPointer":"/testing/blah","proxy":{"blah":42}}]

p.arr = [];   
// Console log:
// [{"type":"add","target":{"hello":"WORLD","testing":{"blah":42},"arr":[]},"property":"arr","newValue":[],"currentPath":"arr","jsonPointer":"/arr","proxy":{"hello":"WORLD","testing":{"blah":42},"arr":[]}}]

p.arr.push("hello world");   
// Console log:
// [{"type":"add","target":["hello world"],"property":"0","newValue":"hello world","currentPath":"arr.0","jsonPointer":"/arr/0","proxy":["hello world"]}]

delete p.hello;  
// Console log:
// [{"type":"delete","target":{"testing":{"blah":42},"arr":["hello world"]},"property":"hello","newValue":null,"previousValue":"WORLD","currentPath":"hello","jsonPointer":"/hello","proxy":{"testing":{"blah":42},"arr":["hello world"]}}]

p.arr.splice(0,1);   
// Console log:
// [{"type":"delete","target":[],"property":"0","newValue":null,"previousValue":"hello world","currentPath":"arr.0","jsonPointer":"/arr/0","proxy":[]},
// {"type":"update","target":[],"property":"length","newValue":0,"previousValue":1,"currentPath":"arr.length","jsonPointer":"/arr/length","proxy":[]}]

console.log(JSON.stringify(test));   
// Console log:
// {"testing":{"blah":42},"arr":[]}

Nested objects

If you wish to observe changes on a parent object and observe changes to an object nested on the parent, you may do so as follows:

var data = {"testing":{"test":{"testb":"hello world"},"testc":"hello again"},"blah":"tree"};

var p = ObservableSlim.create(data, true, function(changes) { console.log("First observable");console.log(changes); });
var pp = ObservableSlim.create(data.testing, true, function(changes) { console.log("Second observable");console.log(changes); });
var ppp = ObservableSlim.create(data.testing.test, true, function(changes) { console.log("Third observable");console.log(changes); });
  • A change to ppp.testb will trigger the callback on all three observables.
  • A change to p.testing.test.testb will also trigger the callback on all three observables.
  • A change to pp.testc will only trigger the first and second observable.
  • A change to p.blah will only trigger the first observable.

Add observers

If you wish to add a second observer function to the same object, you may do so as follows:

// First, create the observable
var test = {};
var proxy = ObservableSlim.create(test, true, function(changes) {
	console.log(JSON.stringify(changes));
});

// Add a new observer function
ObservableSlim.observe(proxy, function(changes) {
	console.log(changes);
});

Pause observers

If you wish to pause the execution of observer functions, you may do so as follows:

ObservableSlim.pause(proxy);

Resume observers

While an observable is paused, no observer functions will be invoked when the target object is modified.

To resume the execution of observer functions:

ObservableSlim.resume(proxy);

Pause changes

If you wish to pause changes to the target data without pausing the execution of the observer functions, you may do so as follows:

ObservableSlim.pauseChanges(proxy);

Resume changes

While an observable has changes paused, all observer functions will be invoked, but the target object will not be modified.

To resume changes:

ObservableSlim.resumeChanges(proxy);

Remove an observable

When you no longer need to use an observable or monitor the object that it targets, you may remove the observable as follows:

ObservableSlim.remove(proxy);

Special features

Proxy check

When using ObservableSlim, you can quickly determine whether or not an object is a proxy by checking the __isProxy property:

var test = {"hello":"world"};
var proxy = ObservableSlim.create(test, true, function(changes) {
	console.log(JSON.stringify(changes));
});

console.log(proxy.__isProxy); // returns true
console.log(test.__isProxy); // undefined property

Look up the original proxied target object

ObservableSlim allows you to easily fetch a reference to the original object behind a given proxy using the __getTarget property:

var test = {"hello":{"foo":{"bar":"world"}}};
var proxy = ObservableSlim.create(test, true, function(changes) {});

console.log(proxy.__getTarget === test); // returns true

Look up a parent object from a child object

ObservableSlim allows you to traverse up from a child object and access the parent object:

var test = {"hello":{"foo":{"bar":"world"}}};
var proxy = ObservableSlim.create(test, true, function(changes) {
	console.log(JSON.stringify(changes));
});

function traverseUp(childObj) {
	console.log(JSON.stringify(childObj.__getParent())); // prints out test.hello: {"foo":{"bar":"world"}}
	console.log(childObj.__getParent(2)); // attempts to traverse up two levels, returns undefined because test.hello does not have a parent object
};

traverseUp(proxy.hello.foo);

Note: This functionality is not supported by the ES5 Proxy polyfill.

Retrieve the path of an object relative to the top-level observer

ObservablesSlim also allows you to retrieve the full path of an object relative to the top-level observed object:

var data = {"foo":"bar","arr":[{"test":{}}],"test":{"deeper":{}}};
var p = ObservableSlim.create(data, false, function(changes) {});

console.log(p.test.deeper.__getPath); // logs "test.deeper"

Note: This functionality is not supported by the ES5 Proxy polyfill.

Requirements

For full functionality, Observable Slim requires ES6 Proxy.

As of August 2017, ES6 Proxy is supported by Chrome 49+, Edge 12+, Firefox 18+, Opera 36+ and Safari 10+. Internet Explorer does not support ES6 Proxy.

ES5 Proxy polyfill (IE11 support)

ObservableSlim now offers limited support for ES5 browsers or browsers without native Proxy (most motably IE11) through the integration of a forked version of the Google Chrome Proxy polyfill.

The forked version of the Proxy polyfill (contained within this repo) differs from the original Polyfill by adding support for the array mutation methods: push, pop, shift, unshift, splice, sort, and reverse.

Limitations

Because the Proxy polyfill does not (and will never) fully emulate native ES6 Proxy, there are certain use cases that will not work when using Observable Slim with the Proxy polyfill:

  1. Object properties must be known at creation time. New properties cannot be added later.
  2. Modifications to .length cannot be observed.
  3. Array re-sizing via a .length modification cannot be observed.
  4. Property deletions (e.g., delete proxy.property;) cannot be observed.

Array mutations can be observed through the use of the array mutation methods listed above.

Contributing

Contributions are most welcome! Please be sure to run gulp test and gulp lint against your code before submitting a pull request.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].