A small Javascript library for events, observable objects, and data binding.
Smack can be used on the server with NodeJS or on the client, built with Browserify, so install with npm:
npm install smack
Eventable
, ChangeNotifier
, and Observable
can be either used directly as
class constructors (e.g, new Eventable()
), or as mixins with something like
typedef.mixin.
Run-of-the-mill events mixin. Similar to the Backbone Events API.
var Eventable = require('smack').Eventable;
var thing = new Eventable();
thing.on('shout', function(message) {
console.log(message + '!');
});
thing.trigger('shout', 'Hello');
// Hello!
var another = new Eventable();
another.listenTo(thing, 'shout', function() {
console.log('thing is shouting!');
});
thing.trigger('shout', 'Hey');
// Hey!
// thing is shouting!
Mixin that uses events to signal to other objects that a change has occured. It serves as a standardized interface for handling object change and property change notifications.
The example below uses a mixin
funciton that simply adds the methods on the
prototype
property of one object to another.
var ChangeNotifier = require('smack').ChangeNotifier;
var mixin_ = require('typedef').mixin;
mixin_(Hero, ChangeNotifier);
function Hero()
{
this.hp = 100;
this.status = 'alive';
}
Hero.prototype.takeDamage(amount)
{
this.hp = Math.max(0, this.hp - amount);
if (this.hp > 0) return;
this.status = 'dead';
this.triggerPropertyChange('status');
}
...
var guy = new Hero();
guy.onPropertyChange({
status: function() { console.log('status is now: ' + guy.status); }
});
An object that has all the methods of Eventable
and ChangeNotifier
mixed
in, with some helpful methods to automatically wire up certain properties to
fire change events.
var Observable = require('smack').Observable;
var Eventable = require('smack').Eventable;
var mixin_ = require('typedef').mixin;
mixin_(Person, Observable);
function Person()
{
}
Person.observable({
name: 'John',
age: 27
});
...
var bob = new Person();
bob.onPropertyChange({
name: function() { console.log('name changed!'); }
});
bob.name = 'Bob'
// name changed!
bob.name = 'Bob';
// [nothing]
bob.name = 'Bobby';
// name changPerson name changeded!
A proxying object that allows you to connect a source to one or more targets.
This allows for a way to keep certain objects in sync via databinding. Assuming
a source implements/mixes in ChangeNotifier
, all targets will be kept in
sync.
// Using the Person class above ...
var me = new Person();
// Create a binding sourcing the 'name' property of the instance 'me'.
var binding = new Binding();
binding.setSource(me, 'name');
var nameTag = { value: '[BLANK]' };
// Proxy all changes from the source (me#name) to the nameTag object
binding.setTarget(nameTag, 'value');
nameTag.value // === '[BLANK]'
me.name = 'Brandon';
nameTag.value // === 'Brandon'
Bindings are extremely useful when creating de-coupled surfaces that you want data to remain in-sync across, without having to manually do the accounting.
The library files are all decorated with JSDoc3-style
annotations that work great with the Tern code inference
system. Combined with the Node plugin (see this project's .tern-project
file), you can have intelligent autocomplete for methods in this library.
Testing is done with Tape and can be run
with the command npm test
.
Automated CI cross-browser testing is provided by Testling.
Copyright 2014 Brandon Valosek
Smack is released under the MIT license.