Skip to content

Instantly share code, notes, and snippets.

@efreed
Created November 20, 2017 16:03
Show Gist options
  • Save efreed/5c327d75638cbe83c08c88193a16f407 to your computer and use it in GitHub Desktop.
Save efreed/5c327d75638cbe83c08c88193a16f407 to your computer and use it in GitHub Desktop.
Knockout Helpers
/**
* Lookup the observable object or value of a field safely no matter if it is static or observable or even exists
* @syntax kov([scope,]period_delimited_lookup_string[,ifmissing])
* @returns value or ifmissing
* @author Eric Freed
*/
function kov(a, b, c, orig_f, orig_scope) {
try {
var ifmissing, f, _scope = "init";
if (typeof a == "string") {
_scope = (ko.contextFor(document.body)||{}).$data;
f = a+"";
ifmissing = b;
} else {
_scope = a;
f = b+"";
ifmissing = c;
}
// Grab first element in a dot-notation field
var path = f.split(".");
f = path.shift();
if (typeof _scope === undefined)
throw "scope is empty";
if ( !(_scope.hasOwnProperty(f) || f in _scope) )
throw "property is undefined";
if (typeof _scope === "string" && !isNaN(f))
throw "you didn't mean to ask for a character in a string, right?";
var field_found = _scope[f];
if (path.length) {
// Drill in to next level
if (typeof field_found == "function")
field_found = field_found();
return kov(field_found, path.join("."), ifmissing, orig_f || (f+"."+(path.join('.'))), orig_scope || _scope);
} else {
if (field_found === undefined)
return ifmissing;
return field_found;
}
} catch(e) {
if (f === undefined)
console.log("error loading kov", a, b, c);
if (ifmissing === undefined) {
if (orig_f) {
console.log("error in kov("+f+")", e, _scope, "_orig_", orig_f, orig_scope);
} else {
console.log("error in kov("+f+")", e, _scope);
}
if (typeof window.Error == "function") {
var err = new Error();
console.log('stack from above error: ', err.stack);
}
if (window.Rollbar) {
Rollbar.warning("error in kov("+f+")", {
js_error: e,
scope: _scope,
orig_search: orig_f,
orig_scope: orig_scope
});
}
}
return ifmissing;
}
}
// Safe shorthand for kov(...)()
function v(a, b, c) {
var v = this.kov(a, b, c);
if (typeof v === "function")
return v();
return v;
}
// Numeric extender from KO's Documentation
ko.extenders.numeric = function(target, precision) {
//create a writable computed observable to intercept writes to our observable
var result = ko.pureComputed({
read: target, //always return the original observables value
write: function(newValue) {
var current = target(),
roundingMultiplier = Math.pow(10, precision),
newValueAsNum = isNaN(newValue) ? 0 : parseFloat(+newValue),
valueToWrite = Math.round(newValueAsNum * roundingMultiplier) / roundingMultiplier;
//only write if it changed
if (valueToWrite !== current) {
target(valueToWrite);
} else {
//if the rounded value is the same, but a different value was written, force a notification for the current field
if (newValue !== current) {
target.notifySubscribers(valueToWrite);
}
}
}
}).extend({ notify: 'always' });
//initialize with current value to make sure it is rounded appropriately
result(target());
//return the new computed observable
return result;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment