obs
v1.0.2
Published
Observable properties done right.
Downloads
29
Readme
Synopsis
obs is a powerful implementation of observable properties that can be used on both the client-side and the server-side.
Together with rivets.js it can serve as a lightweight alternative to Knockout.js.
Install
Node.js
With NPM
npm install obs
From source
git clone https://github.com/pluma/obs.git
cd obs
npm install
make && make dist
Browser
With component
component install pluma/obs
With a CommonJS module loader
Download the latest minified CommonJS release and add it to your project.
Make sure you also have a compatible copy of sublish.
Learn more about CommonJS modules.
With an AMD module loader
Download the latest minified AMD release and add it to your project.
Make sure you also have a compatible copy of sublish.
As standalone bundle
Get the latest distribution bundle (~4.8 kB minified or ~1.4 kB gzipped, includes sublish 2.0.0) and download it to your project.
<script src="/your/js/path/obs.all.min.js"></script>
This makes the obs
module available in the global namespace.
If you are already using sublish
in your project, you can download the latest minified standalone release (~4.0 kB minified or ~1.2 kB gzipped) instead.
Basic usage example with node.js
var obs = require('obs');
var x = obs.prop(2),
y = obs.prop(5),
sum = obs.computed(function() {
return x() + y();
}, [x, y]),
product = obs.computed(function() {
return x() * y();
}, [x, y]);
console.log('sum is currently ' + sum());
// 'sum is currently 7'
console.log('product is currently ' + product());
// 'product is currently 10'
sum.subscribe(function(value, old) {
console.log('sum is now ' + value + ' (was: ' + old + ')');
});
product.subscribe(function(value, old) {
console.log('product is now ' + value + ' (was: ' + old + ')');
});
x(3);
// 'sum is now 8 (was: 7)'
// 'product is now 15 (was: 10)'
console.log('sum is currently ' + sum());
// 'sum is currently 8'
y(8);
// 'sum is now 11 (was: 8)'
// 'product is now 24 (was: 15)'
Example with writable computed observables
var obs = require('obs');
var firstname = obs.prop('John'),
lastname = obs.prop('Doe'),
fullname = obs.computed({
compute: function() {
return firstname() + ' ' + lastname();
},
write: function(value) {
var tokens = (value || '').split(' ');
firstname(tokens[0]);
lastname(tokens.slice(1).join(' '));
},
watch: [firstname, lastname]
});
console.log(fullname()); // John Doe
fullname('Konrad von Zuse');
console.log(firstname()); // Konrad
console.log(lastname()); // von Zuse
Client-side example with rivets.js data-binding
Try it on jsfiddle.
HTML
<div id="view">
<label>
Your name:
<input data-rv-value="user.name"/>
</label>
<div data-rv-bgcolor="color">
Hello <span data-rv-text="user.name"></span>!<br>
The current UNIX time is: <span data-rv-text="now"></span>
</div>
</div>
CSS
#view {
font: 16px Verdana, Arial, sans-serif;
}
#view div {
padding: 10px;
}
JavaScript
Utilities
var colors = [
'rgba(255,0,0,0.5)', 'rgba(255,255,0,0.5)',
'rgba(0,255,0,0.5)', 'rgba(0,255,255,0.5)',
'rgba(0,0,255,0.5)', 'rgba(255,0,255,0.5)'
];
function resolveKeypath(obj, keypath) {
keypath.split('.').forEach(function(key) {
if (key) {
obj = obj[key];
}
});
return obj;
}
Rivets.js adapter and configuration
rivets.configure({
prefix: 'rv',
adapter: {
subscribe: function(obj, keypath, callback) {
resolveKeypath(obj, keypath).subscribe(callback);
},
unsubscribe: function(obj, keypath, callback) {
resolveKeypath(obj, keypath).unsubscribe(callback);
},
read: function(obj, keypath) {
return resolveKeypath(obj, keypath)();
},
publish: function(obj, keypath, value) {
resolveKeypath(obj, keypath)(value);
}
}
});
rivets.binders.bgcolor = function(el, value) {
el.style.backgroundColor = value;
};
ViewModel (using obs.js)
var viewModel = {
now: obs.prop(+new Date()),
color: obs.prop(colors[0]),
user: {
name: obs.prop('User')
}
};
var view = rivets.bind($('#view'), viewModel);
setInterval(function() {
viewModel.now(+new Date());
viewModel.color(colors[
Math.floor(Math.random() * colors.length)
]);
}, 3000);
API
obs: Abstract observables
This provides the base functionality for observables. You probably want to use obs.prop
and obs.computed
instead of calling obs
directly.
obs(options):Function
Creates an observable.
options.context (optional)
The context the observable's read
and write
functions will be executed in.
Defaults to the observable instance if not explicitly set.
options.read:Function and options.write:Function (optional)
The functions to be called when the observable is read from or written to.
An error will be raised if the observable is read from but no read
function was defined, or if it is written to and no write
function was defined.
options.watched:Array (optional)
An array of objects this observable subscribes to. If the value is not an array, it will be wrapped in one. Each object should have a subscribe
method and (optionally) an unsubscribe
method.
options.onNotify:Function (optional)
Function to be called when an object the observable is watching changes. Defaults to the observable's notify
method, effectively turning the observable into a relay.
observable()
Calls the observable's read
function with its context
.
observable(value)
Calls the observable's write
function with its context
and the given value
.
observable.subscribe(callback:Function)
Adds the given callback function to this observable's list of subscribers.
The callback will be called with the observable's new and old value as its arguments whenever the observable's value is updated (even if the new value is equal to the old value).
observable.unsubscribe(callback:Function):Boolean
Removes the given callback function from this observable's list of subscribers. The callback will no longer be called when the observable's value changes.
Returns false
if the callback could not be found in the list of subscribers or true
otherwise.
NOTE: Remember to use the exact function that was passed to observable.subscribe
.
observable.peek()
Returns the observable's current value without invoking its read
function.
observable.commit()
Sets the observable's initial value to its current value and clears its dirty
flag.
observable.reset()
Resets the observable to its initial value (or undefined
), then calls notify()
.
observable.notify()
Updates the observable's dirty
flag, then notifies all subscribers with the its current and previous value.
observable.watch(dependencies…)
Adds the given dependencies to this observable. Each dependency should have a subscribe
and unsubscribe
method. Whenever one of the dependencies changes, this observable's onNotify
function will be called.
observable.unwatch(dependencies…)
Removes the given dependencies by calling their unsubscribe
methods. The observable will no longer be notified when their values change.
observable.dismiss()
Removes all of the observable's dependencies. Equivalent to calling observable.unwatch
for each dependency.
obs.fn
An object containing attributes that will be applied to new observables.
obs.prop: Observable properties
This provides a simple wrapper around obs
useful for observables that should just act as a single value storage.
obs.prop([initialValue])
Creates an observable property (optionally initialized with the given value).
obs.prop#()
Returns the property's current value.
obs.prop#(newValue)
Sets the property's current value to newValue
and notifies all subscribers.
obs.computed: Computed observables
This provides a simple wrapper around obs
to create observables that depend on other observables and have values that should be computed dynamically, e.g. composite values of other observables.
obs.computed(compute:Function, [write:Function], [watch:Array])
Creates a computed observable observable. The observable's value will be set to the return value of the given function compute
and updated whenever any of the watch
functions changes.
If write
is passed, that function will be used when the computed observable is passed a value.
The list of watch
functions can be an array containing any kind of object that supports the subscribe
and (optionally) unsubscribe
methods (e.g. an instance of sublish.PubSub
).
obs.computed(options)
Creates a computed observable property with the given options.
options.compute:Function (optional)
The function this computed observable will use to generate its value. If this option is not provided, the observable will be write-only.
options.write:Function (optional)
The function this computed observable will use when it is passed a value. If this option is not provided, the observable will be read-only.
options.watch:Array (optional)
See above. This option has no effect if no read
function is provided. If a single object is passed instead of an array, the object will automatically be wrapped in an array.
options.context (optional)
The context the compute
and write
functions will be executed in. Defaults to the computed observable itself.
computedObservable()
Returns the computed property's current value. For lazy computed observables, this will trigger the function evaluation and notify any subscribers.
obs.computed.lazy(compute:Function, [write:Function], [watched:Array])
See obs.computed(…)
. The created observable will only call its compute
function to update its value when it is explicitly read from.
obs.computed.lazy(options)
See obs.computed(options)
and above.
License
The MIT/Expat license. For more information, see http://pluma.mit-license.org/ or the accompanying LICENSE file.