Skip to content

Instantly share code, notes, and snippets.

@dandean
Created August 7, 2009 07:56
Show Gist options
  • Save dandean/163773 to your computer and use it in GitHub Desktop.
Save dandean/163773 to your computer and use it in GitHub Desktop.
Experimental mixins for decoupled object property watching
// This has been taken further here:
// http://github.com/dandean/shouter/tree/master
/**
* mixin Watchable enables sproutcore-style property watching... kinda.
* todo: - unwatch
* - allow property modification before setting
* - revisit naming
* - examples with UI elements
* - investigate potential performance issues
* - _reactTo_$ method for props without specific handlers
* - Make Element.addMethods(Watchable) work
**/
var Watchable = (function() {
var _registry = [];
return {
/**
* Watchable#set(prop, value) -> "undefined"
* Sets the property on this object to the specified value, then notifies all watchers.
**/
set: function(prop, value) {
if (this[prop] != value) {
var old = this[prop];
this[prop] = value;
_registry.invoke("_react", this, prop, old, this[prop]);
}
return this;
},
/**
* Watchable#get(prop) -> object
* Gets the value from the object.
**/
get: function(prop) {
return this[prop];
},
/**
* Watchable#watcher(watcher) -> "undefined"
* Adds a watcher to noticy when properties change.
**/
watcher: function(watcher) {
if (!("_react" in watcher)) throw "'watcher' does not extend Watcher class.";
_registry.push(watcher);
}
};
})();
/**
* class Watcher
* todo: This could probably just be a Mixin instead of a Class.
**/
var Watcher = Class.create({
/**
* new Watcher();
**/
initialize: function() { },
/**
* Calls property-specific handlers when possible.
**/
_react: function(source, prop, old, value) {
var callback = "_reactTo" + prop.charAt(0).toUpperCase() + prop.substr(1);
if (callback in this) this[callback](source, old, value);
},
toString: function() { return "[object Watcher]"; }
});
// ------------------------
/**
* Dummy classes to implement watching...
**/
var MyWatchable = Class.create(Watchable, {
initialize: function() { },
toString: function() { return "[object MyWatchable]"; }
});
var MyWatcher = Class.create(Watcher, {
initialize: function(watchable) {
watchable.watcher(this);
},
/**
* Handles when the "test1" property is set
**/
_reactToTest1: function(source, old, value) {
console.log("test1 property set to #{value} (was #{old}) by:"
.interpolate({old: old || "undefined", value: value}), source);
},
/**
* Handles when the "test2" property is set
**/
_reactToTest2: function(source, old, value) {
console.log("test2 property set to #{value} (was #{old}) by:"
.interpolate({old: old || "undefined", value: value}), source);
},
/**
* Handles when the "innerHTML" property is set
**/
_reactToInnerHTML: function(source, old, value) {
console.log("innerHTML property set to #{value} (was #{old}) by:"
.interpolate({old: old || "undefined", value: value}), source);
},
toString: function() { return "[object MyWatcher]"; }
});
// Make it go!
var watchable = new MyWatchable();
var watcher = new MyWatcher(watchable);
console.info("Set properties. Should see 2 responses...");
watchable.set("test1", "cool").set("test2", 5);
// ------------------------
var watcher2 = new MyWatcher(watchable);
console.info("Added another watcher. Should now see 4 responses...");
watchable.set("test1", "amazing").set("test2", 6);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment