Last active
May 18, 2018 14:39
-
-
Save kevincennis/25ba31b2e9f9c8b7a34047ba0108aff0 to your computer and use it in GitHub Desktop.
ProxyModel.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
const Model = ( () => { | |
// event cache | |
const events = new WeakMap(); | |
// proxy handler | |
const handler = { | |
set( target, key, value, receiver ) { | |
if ( target.validators && target.validators[ key ] ) { | |
if ( !target.validators[ key ]( value ) ) { | |
throw new TypeError(`value ${ value } for key ${ key } is invalid`); | |
} | |
} | |
receiver.emit( 'change', key, value ); | |
return target[ key ] = value; | |
} | |
}; | |
// Model class | |
class Model { | |
constructor( opts ) { | |
const proxy = new Proxy( this, handler ); | |
Object.assign( this, opts ); | |
events.set( proxy, Object.create( null ) ); | |
return proxy; | |
} | |
on( evt, fn ) { | |
const cache = events.get( this ); | |
const evts = cache[ evt ] || ( cache[ evt ] = [] ); | |
evts.push( fn ); | |
return this; | |
} | |
emit( evt, ...args ) { | |
const cache = events.get( this ); | |
if ( cache[ evt ] ) { | |
cache[ evt ].forEach( fn => fn.call( this, ...args ) ); | |
} | |
return this; | |
} | |
off( evt, fn ) { | |
if ( arguments.length === 0 ) { | |
events.set( this, {} ); | |
return this; | |
} | |
if ( arguments.length === 1 ) { | |
delete events.get( this )[ evt ]; | |
return this; | |
} | |
const cache = events.get( this ); | |
const evts = cache[ evt ]; | |
if ( evts ) { | |
cache[ evt ] = evts.filter( f => f !== fn ); | |
} | |
return this; | |
} | |
} | |
return Model; | |
})(); | |
// usage... | |
let model = new Model({ | |
validators: { | |
baz: value => value === 'bing' | |
} | |
}); | |
model.on( 'change', ( key, value ) => console.log( `change ${ key }: ${ value }` ) ); | |
model.baz = 'bing'; // works, emits a change event | |
model.baz = 'boop'; // fails validation, throws a TypeError |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment