lell

1.2.5 • Public • Published

lell - Build Status Latest Stable Version NPM Downloads Build Status Code Climate License

Make all your data observable without changing the way you do anything

Very small learning curve, enormous power.

Quick Use

Pop a model, subclassing Le is better but unnecessary

// model.js
import {Le, Ll} from 'lell'
 
 
var livingPerson = new Le({name:'z', power_level:9000})
// or
var livingPerson = new Person({name:'z',power_level:9000}) // if person extends Le
 
 
module.exports = {
  person:livingPerson
}

Read and subscribe!

// components/person.js
var model = require('./model.js')
 
class Person extends React.Component {
  constructor(props) {
    super(props)
    this.state = {person:model.person}
  }
  componentDidMount() {
    model.person.subscribe((person) => this.setState({person:person}))
  }
  tapIncreasePowerLevel() {
    model.person.power_level++
    // auto updates view
  }
  render() {
    var person = model.person
    return <div>
    <span>{person.name}</span> - {person.power_level}
    <button onClick={this.tapIncreasePowerLevel} />
    </div>
  }
}
 

Installation

Via npm:

npm install --save lell

Then require/import:

import {Le, Ll} from 'lell'
//or
var Le = require('lell').Le
var Ll = require('lell').Ll

Usage

Objects/Classes

Quick Living Entity

Just instantiate an Le with a payload

The only keys that cause events are the ones passed into the constructor

import {Le} from 'lell'
 
var aPerson = {name:'z',power_level:9000}
 
var aLivingPerson = new Le(aPerson)
 
aLivingPerson.subscribe((p) => console.log(p.power_level))
 
aLivingPerson.power_level++
// console output: 9001
 
An observable class

Extending an Le is only necessary if you can't init with all your properties or you just want convenience methods (like ajax requests) The living properties are only the ones we initialize with, so we have have to make sure our initial state holds all keys we wish to cause updates which is set in the _defaults method

import {Le} from 'lell'
import _ from 'lodash'
 
class Person extends Le {
  _defaults() {
    // having defaults makes these fields observable even if you haven't initialized with them
    return {name:'', power_level:0}
  }
  doSomeAsyncWork() {
    superagent
      .post('/increase_powerlevel')
      .send(this)
      .end((err, data) => {
        this.power_level++
    })
  }
}
 
var aLivingPerson = new Person()
 
aLivingPerson.subscribe((p) => console.log(p.name))
 
aLivingPerson.name = 'z'
// z
 
aLivingPerson.doSomeAsyncWork()
// z
Entity Mapping

Provide a map in your Le subclass so that an initial payload is initialized with Le's

class Person extends Le {
  _map() {
    return {friends:[Person], bestFriend:Person}
  }
}
 
var friendlyPerson = new Person({
  name:'z',
  bestFriend:{
    name:'s',
    power_level:9000
  },
  friends:[
    {
      name:'b',
      power_level:9001
    },
    {
      name:'n',
      power_level:9002
    }
  ]
})
// subscribe to bestFriend changes
friendlyPerson.bestFriend.subscribe((bf) => console.log(bf.power_level))
friendlyPerson.friends[0].subscribe((f) => console.log(f.power_level))
Some state information

Le's have state information you can use to help your web interfaces, ie, agnostic new/edit react views

var p = new Person()
console.log(p._state)
//new
p.power_level = 9000
console.log(p._state)
//new
console.log(p._updates)
// ["power_level"]
p._commit()
console.log(p._state)
// original
console.log(p._updates)
// []
 
var ep = new Person({name:'z', power_level:9000})
console.log(ep._state)
// original
ep.power_level++
console.log(ep._state)
// updated
ep.power_level--
console.log(ep._state)
// original
ep.power_level++
console.log(ep._state)
// updated
ep._commit()
console.log(ep._state)
// original
console.log(ep._updates)
// []
Rx.Observable available

The Rx observable subject is made available to you:

aLivingPerson.subject.pluck('power_level').subscribe((power_level) => console.log(power_level))
 
aLivingPerson.power_level = 9000
// 9000
Silent Updates

Update your instance without kicking off an update

aLivingPerson.subscribe((p) => console.log(p))
 
aLivingPerson.silentUpdate('power_level', 8999)
// no events

Lists

You can easily create living lists (Ll) that send notifications when the underlying array changes

You need to make changes through the array through the Lls methods addItems, removeItems, setItems

Quick list

We can make a simple living list

import {Ll} from 'lell'
//or
var Ll = require('lell').Ll
 
var abe = ...
var bush = ...
var people = [abe, bush]
 
var livingPeople = new Ll({people}) // this means {people: people}
 
livingPeople.subscribe((people) => console.log(people.length))
 
var kennedy = ...
 
livingPeople.addItems(kennedy)
// 3
livingPeople.removeItems([abe, bush])
// 1
livingPeople.setItems([abe, kennedy])
// 2
Sorted List

This is where Lls start getting good, you can supply a sort key/func to your Ll and your list will be sorted (borrows lodash _.sortBy)

 
var people = [{name:'z', power_level:9001}, {name:'y', power_level:9000}]
var livingPeople = new Ll({people, sort:'power_level'})
 
livingPeople.subscribe((people) => console.log(people[0].name))
 
livingPeople.addItems({name:'x', power_level:8999})
// x
Meant for Le's

Okay, that wasn't that cool, but Ll's are best when holding Le's

var livingPersonZ = new Person({name:'z',power_level:8999})
var livingPersonY = new Person({name:'y',power_level:9000})
 
var livingPeople = new Ll({
  people:[livingPersonZ, livingPersonY],
  sort:'power_level'
  })
 
console.log(livingPeople.people[1].name)
// y
 
livingPeople.subscribe((people) => console.log(people[1].name))
 
livingPersonZ.power_level = 9001
// z
 

Cool, huh? We have a reactive list

Subscribe each

You can listen to all of the items in a list, so when any Le in an Ll changes, you can be notified with that Le

 
livingPeople.subscribeEach((aPerson) => console.log(aPerson.power_level))
 
livingPersonZ.power_level++
// 9002
 
Rx.Observable availability

The observables are available to you, one for the list, subject, and one for the subscribeEach, itemChangeSubject

// note, you need to pluck people, because the root subject signals with the Le {people, sort}
livingPeople.subject.pluck('people').map(doSomething).subscribe(listen)
 
livingPeople.subject.pluck('power_level').average().subscribe(useAverage)
Subclassing is favorable

It really makes things convenient

import {Ll} from 'lell'
import _ from 'lodash'
 
var defaultState = {
  people:[personA, personB],
  sort:'power_level'
}
 
class People extends Ll {
  constructor(state) {
    state = state ? _.defaults(state, defaultState) : Object.assign({}, defaultState)
    super(state)
  }
}
 
var initialState = {people:[personA, personC, personX]}
var livingPeople = new People(people)
 
livingPeople.subscribe()

Future

  • Auto Hydrate/Serialize entire Le/Ll store between server/client
  • Le actions

License

The MIT License (MIT)

Copyright (c) 2016 ark

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Package Sidebar

Install

npm i lell

Weekly Downloads

5

Version

1.2.5

License

MIT

Unpacked Size

55.4 kB

Total Files

20

Last publish

Collaborators

  • zedkn