Skip to content

Instantly share code, notes, and snippets.

@far-blue
Created June 14, 2016 12:57
Show Gist options
  • Save far-blue/20e368e175b05429a054d011f02830ad to your computer and use it in GitHub Desktop.
Save far-blue/20e368e175b05429a054d011f02830ad to your computer and use it in GitHub Desktop.
First attempt at creating a custom observable for Knockout to wrap a NestedTypes model instance attribute efficiently.
function modelAttributeObservable(model, attribute)
{
// Check the attribute exists
if (!(attribute in model)) {
throw new ReferenceError(attribute + ' is not an attribute of the supplied model');
}
// Setup the attribute observable cache
model._koObservables || (model._koObservables = {});
// If we already have a cached observable then just return it
if (attribute in model._koObservables) {
return model._koObservables[attribute];
}
// Create our observable getter/setter function
function observableAttribute()
{
if (arguments.length > 0) {
observableAttribute.valueWillMutate();
model[attribute] = arguments[0];
return this;
} else {
// The caller only needs to be notified of changes if they did a "read" operation
ko.computedContext.oc(observableAttribute);
return observableAttribute.peek();
}
}
// Create a function to fetch the attribute value that
// is in the correct context so it has access to the model.
observableAttribute.peek = function() {
return model[attribute];
}
// Register to listen to change events on the model attribute
model.on('change:'+attribute, function() {
observableAttribute.valueHasMutated();
});
// Initialise the observableAttribute as a subscribable
// Then override the prototype to make it a subclass
ko.subscribable.call(observableAttribute);
observableAttribute.__proto__ = observableAttribute_fn;
// setup the deferred update extender if needed
ko.options['deferUpdates'] && ko.extenders['deferred'](observableAttribute, true);
// Cache the observableAttribute function and return it
return (model._koObservables[attribute] = observableAttribute);
};
// Setup the prototype for the observableAttribute as a subclass of subscribable
observableAttribute_fn = {
__proto__: ko.subscribable['fn'],
valueHasMutated: function () { this.notifySubscribers(this.peek()); },
valueWillMutate: function () { this.notifySubscribers(this.peek(), 'beforeChange');},
// Make KO treat the observableAttribute function as an observable
'__ko_proto__': ko.observable
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment