Skip to content
/ edata Public

Turn javascript data into observable reactive EventEmitter with value getter/setter, lodash style path, and keep Event Sourcing in mind.

License

Notifications You must be signed in to change notification settings

futurist/edata

Repository files navigation

EDATA (Enhanced DATA)

edata is the nested observable reactive EventEmitter with .value getter/setter, lodash style path, and keep Observer Pattern in mind.

It roughly referenced Object.observe API, but instead using getter/setter to wrap object, lightweight than Proxy.

Build Status NPM Version

Install

NPM

npm install --save edata
import edata from 'edata'

Usage

- Quick Start

Below can give you a quick idea of edata:

var root = edata({a: {b: {c: {}}}})
// plain_object ---> edata

Complete example:

import edata from 'edata'
const root = edata({
    age: 20,
    firstName: 'Hello',
    lastName: 'World',
    address: {
        city: 'Earth'
    }
})
const callback = ({type, path}) => console.log(`--> ${type}: ${path}`)
root.watch(callback)
root.set('address.city', 'Moon') // LOG: --> update: ['address', 'city']
root.get('address.city').value // Moon

Plain object wrapped into edata:

edata = new EventEmitter(object)

so use edata.on can watch changes, use edata.value(getter/setter) to get nested edata.

root.value.firstName.value  // get -> firstName
root.value.firstName.value = 'name'  // set -> firstName
root.value.address.value.city.on('change', callback)  // watch on 'change'

Can also use lodash style:

root.get('firstName').value  // get: firstName
root.set('firstName', 'name')  // set: firstName
root.get('address.city').on('change', ...) // watch change

Proxy usage:

use .proxy() to shorten the path:

const data = root.proxy()
data.address.city // Moon
data.__edata__  // get back the `root` edata

- Watch changes

Any edata can watch data changes inside(any level):

const onDataChange = ({data, type, path})=>{
    console.log('value mutated:', path, type, data.unwrap())
}
const unwatch = root.watch('change', onDataChange)

root.set('address.city', 'Mars')
// [LOG] data mutated: [ 'address', 'city' ] add Mars
unwatch() // stop watch
root.get('address.city').value = 'Earth'
// nothing outout

Watch single data change event:

root.get('firstName').on('change', ({path, type, data})=>{
    console.log(path, type, 'to: ' + data)
})
root.set('firstName', 'Hi')
//[LOG] firstName update to: Hi

Note: edata.on('change', callback) has shortcut: edata.map(callback)

- Define Data Relations

You can define data relations using setComputed, as below:

const root = edata({
  firstName: 'Hello',
  lastName: 'World'
})
root.setComputed(
  'fullName',
  ['firstName', 'lastName'],
  ([firstName, lastName]) => firstName + ' ' + lastName
)
assert.equal(root.unwrap('fullName'), 'Hello World')
root.set('firstName', 'Green')
assert.equal(root.unwrap('fullName'), 'Green World')

- Flat data

Since the edata objects nested as EventEmitter, you can flat this structure into plain object using edata.unwrap():

root.unwrap()
// flat root: {age: 20, firstName: 'Hello', lastName: 'World', address: {city: 'Earth'}}

root.unwrap('address')
// flat address: {city: 'Earth'}

root.unset('address')
// delete address

root.unwrap()
// {age: 20, firstName: 'Hello', lastName: 'World'}

Also exist edata.toJSON(), so the JSON.stringify(edata) just like

JSON.stringify(edata.unwrap({json:true}))

- Use in React

The pros is you can use edata.set() instead of this.setState:

import edata from 'edata'
const root = edata({user: {name: 'earth'}})

class App extends React.Component {
    constructor(props){
        super(props)
        const {model} = this.props
        this.state = model.unwrap()
        // state: {name: 'earth'}
        model.watch(()=>{
            this.setState(model.unwrap())
        })
    }
    
    render(){
        const {model} = this.props
        const name = model.unwrap('name')
        return <div>
            <h3>Name: {name}</h3>
            <input
              value={name}
              onChange={e=>{model.set('name', e.target.value)}}
            />
        </div>
    }
}

ReactDOM.render(<App edata={root.cut('user')} />, app)

You can play with the demo here

API

See API Document

About

Turn javascript data into observable reactive EventEmitter with value getter/setter, lodash style path, and keep Event Sourcing in mind.

Resources

License

Stars

Watchers

Forks

Packages

No packages published