simple-bound
Advanced tools
Comparing version 0.0.2 to 0.0.3
@@ -65,3 +65,3 @@ 'use strict'; | ||
/** | ||
* Stores subscribers on change-events for further manipulations. | ||
* Stores subscribers for further manipulations. | ||
*/ | ||
@@ -282,2 +282,11 @@ this.subscribers = []; | ||
} | ||
/** | ||
* [NOT_IMPLEMENTED] Maps the object of a different shape to the original binding object | ||
* @param obj target object to bind | ||
* @param mapToOriginal a map for target object's keys relative to the original binding object type | ||
* @param twoWay whether the binding should be two-way | ||
*/ | ||
BaseBound.prototype.bindAndMap = function (obj, mapToOriginal, twoWay) { | ||
throw new BoundError('Method not implemented'); | ||
}; | ||
Object.defineProperty(BaseBound, "config", { | ||
@@ -284,0 +293,0 @@ /** |
@@ -33,3 +33,3 @@ class BoundError extends Error { | ||
/** | ||
* Stores subscribers on change-events for further manipulations. | ||
* Stores subscribers for further manipulations. | ||
*/ | ||
@@ -243,2 +243,11 @@ this.subscribers = []; | ||
/** | ||
* [NOT_IMPLEMENTED] Maps the object of a different shape to the original binding object | ||
* @param obj target object to bind | ||
* @param mapToOriginal a map for target object's keys relative to the original binding object type | ||
* @param twoWay whether the binding should be two-way | ||
*/ | ||
bindAndMap(obj, mapToOriginal, twoWay) { | ||
throw new BoundError('Method not implemented'); | ||
} | ||
/** | ||
* Global binding config. Changes affect all instances. | ||
@@ -245,0 +254,0 @@ */ |
@@ -64,3 +64,3 @@ var bound = (function (exports) { | ||
/** | ||
* Stores subscribers on change-events for further manipulations. | ||
* Stores subscribers for further manipulations. | ||
*/ | ||
@@ -281,2 +281,11 @@ this.subscribers = []; | ||
} | ||
/** | ||
* [NOT_IMPLEMENTED] Maps the object of a different shape to the original binding object | ||
* @param obj target object to bind | ||
* @param mapToOriginal a map for target object's keys relative to the original binding object type | ||
* @param twoWay whether the binding should be two-way | ||
*/ | ||
BaseBound.prototype.bindAndMap = function (obj, mapToOriginal, twoWay) { | ||
throw new BoundError('Method not implemented'); | ||
}; | ||
Object.defineProperty(BaseBound, "config", { | ||
@@ -283,0 +292,0 @@ /** |
@@ -67,3 +67,3 @@ (function (global, factory) { | ||
/** | ||
* Stores subscribers on change-events for further manipulations. | ||
* Stores subscribers for further manipulations. | ||
*/ | ||
@@ -284,2 +284,11 @@ this.subscribers = []; | ||
} | ||
/** | ||
* [NOT_IMPLEMENTED] Maps the object of a different shape to the original binding object | ||
* @param obj target object to bind | ||
* @param mapToOriginal a map for target object's keys relative to the original binding object type | ||
* @param twoWay whether the binding should be two-way | ||
*/ | ||
BaseBound.prototype.bindAndMap = function (obj, mapToOriginal, twoWay) { | ||
throw new BoundError('Method not implemented'); | ||
}; | ||
Object.defineProperty(BaseBound, "config", { | ||
@@ -286,0 +295,0 @@ /** |
@@ -26,3 +26,3 @@ "use strict"; | ||
/** | ||
* Stores subscribers on change-events for further manipulations. | ||
* Stores subscribers for further manipulations. | ||
*/ | ||
@@ -29,0 +29,0 @@ this.subscribers = []; |
@@ -37,2 +37,11 @@ "use strict"; | ||
/** | ||
* [NOT_IMPLEMENTED] Maps the object of a different shape to the original binding object | ||
* @param obj target object to bind | ||
* @param mapToOriginal a map for target object's keys relative to the original binding object type | ||
* @param twoWay whether the binding should be two-way | ||
*/ | ||
bindAndMap(obj, mapToOriginal, twoWay) { | ||
throw new boundError_1.default('Method not implemented'); | ||
} | ||
/** | ||
* Global binding config. Changes affect all instances. | ||
@@ -39,0 +48,0 @@ */ |
@@ -63,3 +63,3 @@ /** | ||
/** | ||
* Stores subscribers on change-events for further manipulations. | ||
* Stores subscribers for further manipulations. | ||
*/ | ||
@@ -66,0 +66,0 @@ readonly subscribers: ISubscriber[]; |
@@ -8,2 +8,23 @@ import Binding, { IBindingPlugin } from '../binding'; | ||
}; | ||
/** | ||
const BoundInstance = new Bound({ | ||
prop: 'bar', | ||
nested: { | ||
prop: 'foo' | ||
} | ||
}); | ||
BoundInstance.bindAndMap({ | ||
test: 'prop', | ||
nestedProp: 'value' | ||
}, { | ||
prop: 'test', | ||
nested: { | ||
prop: 'nestedProp' | ||
} | ||
}); | ||
*/ | ||
export declare type BindObjectMap<T extends object> = { | ||
[key in keyof T]: T[key] extends object ? BindObjectMap<T[key]> : string; | ||
}; | ||
export default abstract class BaseBound<T extends object> { | ||
@@ -38,2 +59,9 @@ plugins?: IBoundPluginMap<T> | undefined; | ||
/** | ||
* [NOT_IMPLEMENTED] Maps the object of a different shape to the original binding object | ||
* @param obj target object to bind | ||
* @param mapToOriginal a map for target object's keys relative to the original binding object type | ||
* @param twoWay whether the binding should be two-way | ||
*/ | ||
bindAndMap<U>(obj: U, mapToOriginal: BindObjectMap<T>, twoWay?: boolean): void; | ||
/** | ||
* Unbinds an object and destroys all of its listeners | ||
@@ -40,0 +68,0 @@ * |
{ | ||
"name": "simple-bound", | ||
"version": "0.0.2", | ||
"description": "A simple and customizable reactive data-binding framework.", | ||
"version": "0.0.3", | ||
"description": "A simple and customizable reactive data-binding library.", | ||
"keywords": [ | ||
@@ -6,0 +6,0 @@ "binding", |
272
README.md
@@ -1,6 +0,8 @@ | ||
# bound | ||
> A simple and customizable reactive binding framework for node and browser. Work in progress. | ||
# simple-bound | ||
> A simple data binding library for node and browser with no dependencies. | ||
[![Travis (.org) branch](https://img.shields.io/travis/KazanExpress/bound/master.svg?style=flat-square)](https://travis-ci.org/KazanExpress/bound) | ||
[![npm](https://img.shields.io/npm/v/simple-bound.svg?style=flat-square)](https://www.npmjs.com/package/simple-bound) | ||
[![](https://img.shields.io/badge/github-repo-lightgray.svg?style=flat-square)](https://github.com/KazanExpress/bound) | ||
[![](https://img.shields.io/badge/dependencies-none-blue.svg?style=flat-square)](https://www.npmjs.com/package/simple-bound?activeTab=dependencies) | ||
[![npm](https://img.shields.io/npm/dt/simple-bound.svg?style=flat-square)](https://www.npmjs.com/package/simple-bound) | ||
@@ -21,26 +23,72 @@ | ||
--- | ||
## Table of contents | ||
- [What is data-binding?](#what-is-data-binding) | ||
- [What is Bound?](#what-is-bound) | ||
- [Installation](#installation) | ||
- [Simple example](#simple-example) | ||
- [How it works](#how-it-works) | ||
- [API](#api) | ||
- [TLDR (examples)](#tldr) | ||
- [Binding](#binding) | ||
- [Constructor](#binding-constructor) | ||
- [Instance](#binding-instance) | ||
- [Static fields](#binding-static-fields) | ||
- [Bound](#bound) | ||
- [Constructor](#constructor) | ||
- [Constructor](#bound-constructor) | ||
- [Instance](#bound-instance) | ||
- [Static fields](#static-fields) | ||
- [Binding](#binding) | ||
- [Constructor](#constructor) | ||
- [Instance](#bound-instance) | ||
- [Static fields](#static-fields) | ||
- [Details & under-the hood](#details) | ||
- [Static fields](#bound-static-fields) | ||
- [Coming Soon](#coming-soon) | ||
--- | ||
## [What is data binding](https://www.wintellect.com/data-binding-pure-javascript/) | ||
## What is Bound? | ||
## Installation & Usage | ||
Bound is a small and customizable library that allows precise data binding management. | ||
But what is data binding? | ||
[Data binding](https://en.wikipedia.org/wiki/Data_binding) is a general technique that binds data sources of two general types (`provider` and `consumer`, `master` and `slave`) and syncronizes them. | ||
For example, let's imagine two objects bound together using this technique: | ||
```js | ||
object1 /* { | ||
property: 'value' | ||
} */ | ||
object2 /* { | ||
property: 'value' | ||
} */ | ||
object1.property = 'new value' | ||
console.log(object2.property) // outputs 'new value' | ||
object2.property = 'another value' | ||
console.log(object1.property) // outputs 'another value' | ||
``` | ||
As we can see here, both objects seem to react to changes in one another, updating their properties reactively. | ||
Most applications and frameworks use this approach to synchronize their models and views, as described in this [article](https://www.wintellect.com/data-binding-pure-javascript/). | ||
But data-binding is not only useful for synchronizing views to models - there are a lot of applications as use-cases for this technique. And `Bound` aims to cover as most of them as possible. | ||
It does not focus entirely on model-view data-binding, but rather tries to encapsulate the whole concept of data binding, allowing you to control where your bindings go and what your bindings do. | ||
### General concepts | ||
All "members" of the data-binding relationship can be called **subscribers**. | ||
There are two general types of data flow between subscribers in data-binding: two-way (when both objects' properties react to each other, example above) and one-way. | ||
In one-way data-binding there exist two types of binding subscribers: | ||
- "masters" - dictate changes to all other subscribers in the relationship. | ||
- "slaves" - accept changes from masters but cannot broadcast/dicate their own changes | ||
`Bound` allows to handle both one-way and two-way data bindings with ease. | ||
--- | ||
## Installation | ||
### Install as dependency | ||
@@ -59,2 +107,6 @@ | ||
import Bound from 'simple-bound' | ||
// You also can import separate modules: | ||
import Binding from 'simple-bound/dist/lib/binding' | ||
import BaseBound from 'simple-bound/dist/lib/bound/base' | ||
``` | ||
@@ -67,14 +119,19 @@ | ||
**UNPKG** | ||
**Script tag** | ||
```html | ||
<script src="https://unpkg.com/simple-bound"></script> | ||
<script src="https://unpkg.com/simple-bound" onload="bound.Bound = bound.default"></script> | ||
<script> | ||
window.Bound = bound.default; | ||
const binding = new bound.Binding(false, 'value'); | ||
</script> | ||
``` | ||
--- | ||
## Simple example | ||
Let's say you want to bind to objects together in a way that a change to one object would change the other. It's very simple to do with `Bound`: | ||
Let's say you want to bind two objects together in a way that a change to one object would automatically change the other. | ||
It's very simple to do with `Bound`: | ||
```js | ||
@@ -90,5 +147,5 @@ const obj1 = { | ||
// Send the proto object to snapshot the structure. | ||
const bound = new Bound(obj); | ||
const bound = new Bound(obj1 /* Used for snapshoting the object structure, not for the actual binding */); | ||
// Bind both objects via Bound instace: | ||
// Bind both objects via Bound instance: | ||
bound.bind(obj1); | ||
@@ -103,2 +160,46 @@ bound.bind(obj2); | ||
--- | ||
## How it works | ||
### Binding relationships | ||
Each time a [new binding relationship](#binding) is created, the data is stored inside that relationship. After that, subscribers can be added to the relationship. | ||
Each "master" subscriber's value points to the value inside the relationship, therefore automatically sharing it with others: | ||
```js | ||
let obj = { test: 'foo' }; | ||
let obj2 = { test: 'foo' }; | ||
const binding = new Binding(/* twoWay */ true, /* defaultValue */ ''); | ||
binding.addSubscriber(obj, 'test'); | ||
binding.addSubscriber(obj2, 'test'); | ||
// Now both obj.test and obj2.test point to the same variable inside `binding` | ||
``` | ||
Each "slave" subscriber's value remains within its original container, but is updated whenever a __master__'s value changes via a [notification](#binding-instance): | ||
```js | ||
let obj = { test: 'foo' }; | ||
let obj2 = { test: 'foo' }; | ||
const binding = new Binding(/* twoWay */ false, /* defaultValue */ ''); | ||
binding.addSubscriber(obj, 'test', 'master'); | ||
binding.addSubscriber(obj2, 'test', 'slave'); | ||
obj2.test = 'bar'; // Nothing happens here, obj.test is still 'foo' | ||
obj.test = 'new value'; // Notification is sent to obj2, updating its `test` property to 'new value' | ||
``` | ||
### Object relationships | ||
The [`Bound`](#bound) class groups relationships together in a form of an object to subscribe all of their fields to changes. | ||
Essentially, it maps all bound object's properties to their [binding relationships](#binding) using internal [storage](#bound-instance) that contains these relationships in a map identical to the object itself. | ||
--- | ||
## API | ||
@@ -108,3 +209,3 @@ | ||
<!-- <details><summary>Click to expand</summary> --> | ||
<details><summary>Click to expand</summary> | ||
@@ -117,10 +218,23 @@ ```js | ||
// Creates a Bound instance from an object snapshot | ||
const bound = new Bound({ | ||
test: 'foo', | ||
nested: { | ||
property: 2 | ||
} | ||
}); | ||
bound.boundObject // The first actual bound object. | ||
bound.storage // Stores bindings in a structure identical to the original object. | ||
let obj = { | ||
test: 'foo'; | ||
test: 'foo' | ||
} | ||
let obj2 = { | ||
test: 'foo'; | ||
test: 'foo' | ||
} | ||
// Creates a Bound instance from the object and returns instance.boundObject | ||
const justABoundObject = bound({ | ||
@@ -165,4 +279,118 @@ test: 'prop' | ||
<!-- </details> --> | ||
</details> | ||
### Binding | ||
> Stores and updates single property bindings. | ||
Can be thought of as a primitive in terms of data-binding. | ||
Its main responsibility is to manage single-property bindings, hence the name - `Binding` as in "one, single binding". | ||
It helps to manipulate bindings on the lowest possible level. | ||
Creation of a `Binding` instance is equivalent to the creation of a new data-binding relationship. | ||
Each newly added member "subscribes" to notifications about changes in the relationship's value. | ||
#### Binding Constructor | ||
```js | ||
new Binding(/* is always two-way */ false, /* default initial value */ 'value') | ||
``` | ||
argument | type | description | ||
-------------|-----------|-------------- | ||
twoWay | `boolean` | Determines if all the bindings associated with the instance should be two-way | ||
defaultValue | `any` | Sets the default value for subscribers that do not have a value. | ||
#### Binding Instance | ||
```js | ||
const instance = new Binding(false, 'value'); | ||
``` | ||
##### Properties | ||
> ```ts | ||
> instance.subscribers: Array<ISubscriber> | ||
> ``` | ||
> | ||
> Contains an array of subscribers in the following format: | ||
> ```js | ||
> { | ||
> obj: subscriberObject, | ||
> prop: 'subscriberObjectPropertyKey', | ||
> role: 'slave' | 'master' | undefined | ||
> } | ||
> ``` | ||
> ```ts | ||
> instance.twoWay: boolean | ||
> ``` | ||
> READ-ONLY | ||
> | ||
> Defines if a binding should always be 2-way and ignore roles. | ||
> ```js | ||
> instance.value | ||
> ``` | ||
> PROTECTED | ||
> | ||
> Stores the value of the binding relationship for "masters". | ||
> ```ts | ||
> instance.plugins: Array<IBindingPlugin> | ||
> ``` | ||
> | ||
> An array of plugins to use. | ||
##### Methods | ||
> ```js | ||
> instance.get() | ||
> ``` | ||
> A generic get function that is applied to subscribers. | ||
> Gets `instance.value`. | ||
> ```js | ||
> intance.set('new value') | ||
> ``` | ||
> A generic set function that is applied to subscribers. | ||
> Sets `instance.value` and notifies subscribers. | ||
> ```js | ||
> instance.notify('value') | ||
> ``` | ||
> Asynchroniously notifies slave subscribers about the value change. | ||
> ```js | ||
> instance.addSubscriber(obj, 'propName', 'master' | 'slave' | undefined) | ||
> ``` | ||
> Binds an object prop and subscribes it to master-subscribers' changes. | ||
> ```js | ||
> instance.addMasterSubscriber(obj, 'propName') | ||
> instance.addSlaveSubscriber(obj, 'propName') | ||
> ``` | ||
> Binds an object prop as a master or slave and subscribes it to master-subscribers' changes | ||
> ```js | ||
> instance.removeSubscriber(obj, 'propName') // by reference | ||
> instance.removeSubscriber(2) // by index | ||
> ``` | ||
> Unbinds an object's property and unsubscribes it from changes. | ||
> ```js | ||
> instance.clearSubscribers() | ||
> ``` | ||
> Clears all subscribers from the binding. | ||
#### Binding static fields | ||
> ```js | ||
> Binding.config === { | ||
> debug: false | ||
> } | ||
> ``` | ||
> Global binding config. Changes affect all instances. | ||
> ```js | ||
> Binding.subscriptionsEqual(subscriber1, subscriber2) | ||
> ``` | ||
> Checks subscribers' objects for reference and prop-name equality. | ||
## Coming Soon | ||
@@ -169,0 +397,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
213310
2233
394