All Projects → KazanExpress → bound

KazanExpress / bound

Licence: MIT license
Data-binding made easy

Programming Languages

typescript
32286 projects

Projects that are alternatives of or similar to bound

janetrs
Rust high level bindings for Janet
Stars: ✭ 36 (+71.43%)
Mutual labels:  binding, bindings
webview
Nim bindings for https://github.com/zserge/webview
Stars: ✭ 91 (+333.33%)
Mutual labels:  binding, bindings
raylib-zig
Manually tweaked, auto generated raylib bindings for zig. https://github.com/raysan5/raylib
Stars: ✭ 122 (+480.95%)
Mutual labels:  binding, bindings
nuklear4j
Java binding for nuklear
Stars: ✭ 61 (+190.48%)
Mutual labels:  binding, bindings
Fritz2
Easily build reactive web-apps in Kotlin based on flows and coroutines.
Stars: ✭ 308 (+1366.67%)
Mutual labels:  reactive, binding
bindkit
Two-way data binding framework for iOS. Only one API to learn.
Stars: ✭ 13 (-38.1%)
Mutual labels:  reactive, binding
simplesquirrel
Yet another simple binding in C++11 for Squirrel scripting language
Stars: ✭ 37 (+76.19%)
Mutual labels:  binding, bindings
necktie
Necktie – a simple DOM binding tool
Stars: ✭ 43 (+104.76%)
Mutual labels:  binding, typescript-library
observable ish
Observable state and events for browser and Flutter.
Stars: ✭ 26 (+23.81%)
Mutual labels:  reactive, binding
WpfExtensions
Some syntactic sugar for Wpf development.
Stars: ✭ 128 (+509.52%)
Mutual labels:  reactive, binding
Rxrealmdatasources
An easy way to bind an RxRealm observable to a table or collection view
Stars: ✭ 154 (+633.33%)
Mutual labels:  reactive, binding
jquery-bindings
Simple two-way data binding using proxies and requestIdleCallback
Stars: ✭ 17 (-19.05%)
Mutual labels:  bindings, two-way-databinding
twitch2dcs
DCS World mod that allows twitch chat to be viewed inside the game
Stars: ✭ 24 (+14.29%)
Mutual labels:  customizable
akka-stream-kafka-template.g8
Template for Akka Streams & Kafka. Default impl: mirror a topic onto another one
Stars: ✭ 14 (-33.33%)
Mutual labels:  reactive
SJLineRefresh
pull-to-refresh by line path
Stars: ✭ 36 (+71.43%)
Mutual labels:  customizable
typijs
The Angular CMS Framework for building fully-featured SPA sites powered by NodeJS and MongoDB with TypeScript
Stars: ✭ 141 (+571.43%)
Mutual labels:  typescript-library
go-tree-sitter
Golang bindings for tree-sitter https://github.com/tree-sitter/tree-sitter
Stars: ✭ 137 (+552.38%)
Mutual labels:  binding
reactiverse
The Reactiverse main entry point
Stars: ✭ 26 (+23.81%)
Mutual labels:  reactive
ooop
OOP has never been sooo professionally over engineered before.
Stars: ✭ 20 (-4.76%)
Mutual labels:  typescript-library
lua.cr
Crystal to Lua bridge
Stars: ✭ 56 (+166.67%)
Mutual labels:  bindings

simple-bound

A simple data binding library for node and browser with no dependencies.

Travis (.org) branch npm Travis (.org) branch npm


npm install --save simple-bound

For more options see installation

Why?

Many JavaScript libraries and frameworks use some form of data-binding under the hood. That binding usually comes in various shapes and sizes, all of which end up in the "vendor" part of your project. But currently there's no truly utilitary and versatile solution. So most of the time you want to use the power of, let's say, two-way binding in you own library or project - you have to code that part from scratch.

Bound aims to change that.

Bound is currently in the alpha state, there might be some a lot of bugs. Feel free to report them in the issues section. 🙂


Table of contents


What is Bound?

Bound is a small and customizable library that allows precise data binding management.

But what is data binding?

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:

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. 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

npm install --save simple-bound
# or
yarn add simple-bound

Import and use

ES

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'

CommonJS

const Bound = require('simple-bound').default;

Script tag

<script src="https://unpkg.com/simple-bound" onload="bound.Bound = bound.default"></script>

<script>
  const binding = new bound.Binding(false, 'value');
</script>

Simple example

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:

const obj1 = {
  prop: 'foo'
};

const obj2 = {
  prop: 'foo'
};

// Send the proto object to snapshot the structure.
const bound = new Bound(obj1 /* Used for snapshoting the object structure, not for the actual binding */);

// Bind both objects via Bound instance:
bound.bind(obj1);
bound.bind(obj2);

obj1.prop = 'bar';
console.log(obj2.prop);
// -> "bar"
// Magic!

How it works

Binding relationships

Each time a new binding relationship 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:

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:

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 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 using internal storage that contains these relationships in a map identical to the object itself.


API

API can seem a bit tricky at first, because the concept of data-binding does not imply any uniformal way of doing it.

We recommend checking out an interactive playground at RunKit for better intuitive understanding of how things work.

TLDR

Click to expand
import Bound, {
  bound,
  Binding
} from 'simple-bound';

// 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'
}

let obj2 = {
  test: 'foo'
}

// Creates a Bound instance from the object and returns instance.boundObject
const justABoundObject = bound({
  test: 'prop'
});

justABoundObject.__bound__ // Bound instance
justABoundObject.__bound__.bind(obj);

justABoundObject.test = 'bar';
console.log(obj.test); // -> "bar"

justABoundObject.__bound__.storage // { test: Binding {} }
justABoundObject.__bound__.boundObject // === justABoundObject

obj = justABoundObject.__bound__.unbind(obj); // obj is now free from bindings


// Binding class
const binding = new Binding(/* twoWay */ false, /* defaultValue */ '');

binding.addSubscriber(obj, 'test', 'master');
binding.addSubscriber(obj2, 'test', 'slave');

// Updates all subscribers
binding.set('test'); // obj.test === 'test' && obj2.test === 'test'

// Master: updates all subscribers
obj.test = 'foo';    // obj2.test === 'foo' && binding.get() === 'foo'

// Slave: updates only itself
obj2.test = 'bar';   // obj.test === 'foo' && binding.get() === 'foo'

// Master: updates all subscribers
obj.test = 'baz';    // obj2.test === 'baz' && binding.get() === 'baz'

binding.removeSubscriber(obj2); // By object reference
binding.removeSubscriber(0); // By index
binding.clearSubscribers();

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

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

const instance = new Binding(false, 'value');
Properties
instance.subscribers: Array<ISubscriber>

Contains an array of subscribers in the following format:

{
  obj: subscriberObject,
  prop: 'subscriberObjectPropertyKey',
  role: 'slave' | 'master' | undefined
}
instance.twoWay: boolean

READ-ONLY

Defines if a binding should always be 2-way and ignore roles.

instance.value

PROTECTED

Stores the value of the binding relationship for "masters".

instance.plugins: Array<IBindingPlugin> 

An array of plugins to use.

Methods
instance.get()

A generic get function that is applied to subscribers. Gets instance.value.

intance.set('new value')

A generic set function that is applied to subscribers. Sets instance.value and notifies subscribers.

instance.notify('value')

Asynchroniously notifies slave subscribers about the value change.

instance.addSubscriber(obj, 'propName', 'master' | 'slave' | undefined)

Binds an object prop and subscribes it to master-subscribers' changes.

instance.addMasterSubscriber(obj, 'propName')
instance.addSlaveSubscriber(obj, 'propName')

Binds an object prop as a master or slave and subscribes it to master-subscribers' changes

instance.removeSubscriber(obj, 'propName') // by reference
instance.removeSubscriber(2) // by index

Unbinds an object's property and unsubscribes it from changes.

instance.clearSubscribers()

Clears all subscribers from the binding.

Binding static fields

Binding.config === {
  debug: false
}

Global binding config. Changes affect all instances.

Binding.subscriptionsEqual(subscriber1, subscriber2)

Checks subscribers' objects for reference and prop-name equality.


Bound

Allows multiple full-object bindings.

Stores bindings and binds objects together, providing the highest possible abstraction level for bindings.

Bound constructor

new Bound({ property: 'value' })

Creates an instance of Bound using a proto object.

Bound instance

const bound = new Binding({ property: 'value' });
Bound properties
bound.storage: IBindingStorage

Stores bindings in a structure that is identical to the binding-prototype-object.

bound.boundObject

A bound object created from a constuctor's snapshot object. Contains an instance of the Bound class itself by the __bound__ key.

Bound methods
bound.bind(obj, /* two-way? */ true)

Binds an object to all other current subscribers. If the second argument is false

bound.unbind(/* object reference */ obj)

Unbinds an object and destroys all of its listeners

Bound static fields
Bound.config === {
  debug: false
}

Global binding config. Changes affect all instances.

Bound.isBound(/* object reference */ obj)

Checks whether an object has already been bound.


Coming Soon

  • Plugins
  • Event listeners
  • Interceptors and pipes (maybe?)
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].