Skip to content

Instantly share code, notes, and snippets.

@xavierzwirtz
Last active August 29, 2015 14:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xavierzwirtz/50d83af969380320ff3c to your computer and use it in GitHub Desktop.
Save xavierzwirtz/50d83af969380320ff3c to your computer and use it in GitHub Desktop.
.binding-error {
border: 4px dashed red !important;
background-color: #F88 !important;
margin-bottom: 0;
}
//changed from :after psuedo to handle form elements.
.binding-error-message {
display: block !important;
padding: 0.33333rem 0.5rem 0.5rem !important;
margin-top: -1px !important;
margin-bottom: 0.88889rem !important;
font-size: 0.66667rem !important;
font-weight: normal !important;
font-style: italic !important;
background: #f04124 !important;
color: white !important;
}
// Ported to JS from Brian Hunts coffee script version.
// coffee script version: http://brianmhunt.github.io/articles/knockout-catching-errors/
/* global define */
var __slice = [].slice;
var isolate_call = function(binding, fn_name, key) {
// binding is the binding object
// fn_name is one of `init` or `update`
// key is the name of the binding
var fn = binding[fn_name];
var wrapped_fn;
if (!fn) {
return void 0;
}
if (!_.isFunction(fn)) {
console.error("%cERROR%c Binding " + key + "." + fn_name +
" is not a function", "color:red", "color:black",
binding, fn_name, fn);
return void 0;
}
wrapped_fn = function() {
var args, e;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
try {
//Pass-through the call to the original handler.
return fn.apply(binding, args);
} catch (_error) {
e = _error;
var node = args[0];
var $node = $(node);
console.error("%cERROR%c: Binding " + key + "." +
fn_name + " error", "color:red", "color: black",
node, e, e.stack);
$node.addClass("binding-error");
$node.after(
'<div class="binding-error-message">This item may not be working as expected.</div>'
);
}
};
return wrapped_fn;
};
var ErrorHandlerBindingProvider = function(ko) {
var that = this;
var builtInBindingHandlers = _.map(ko.bindingHandlers, function(value,
key) {
return key;
});
that.attach = function() {
var isolate_binding_errors = function(binding, key) {
var isDefault = _.isUndefined(_.find(builtInBindingHandlers,
function(
builtInKey) {
return builtInKey === key;
})) === false;
if (!isDefault) {
binding.init_fn = binding.init;
binding.update_fn = binding.update_fn;
binding.init = isolate_call(binding, 'init', key);
binding.update = isolate_call(binding, 'update', key);
}
};
_.each(ko.bindingHandlers, isolate_binding_errors);
};
};
var CustomBindingHandlerErrorHandler = function(ko) {
// The original binding handler still does the real work; you
// can use your own or an alternative such as knockout-secure-binding
// here though.
var orig;
orig = new ko.bindingProvider();
// We also pass through the `nodeHasBindings` call.
this.nodeHasBindings = orig.nodeHasBindings;
// This returns a map of binding names to respective handlers, given a node
// and the bindingContext.
this.getBindings = function(node, bindingContext) {
var addErrorMessage = function() {
var $node = $(node);
$node.addClass("binding-error");
$node.after(
'<div class="binding-error-message">This item may not be working as expected.</div>'
);
};
var bindings_map, err;
try {
// The bindings_map will be `null` if there are no bindings, otherwise
// an object mapping { name: binding }.
bindings_map = orig.getBindings(node, bindingContext);
} catch (_error) {
// When there is an error, throw out some useful context.
err = _error;
console.error("Binding error: ", err.message, "\nNode:", node,
"\nContext:", ko.contextFor(node));
addErrorMessage();
return null;
}
// Pass null straight up.
if (bindings_map === null) {
return null;
}
// If there isn't at least one good binding, print some helpful
// debugging information.
if (!_(bindings_map).keys().any(function(m) {
return _.has(ko.bindingHandlers, m);
})) {
console.error("No bindings found:", bindings_map, "Node:", node);
addErrorMessage();
return null;
}
return bindings_map;
};
};
});
ko.bindingProvider.instance = new ErrorHandlerBindingProvider(ko);
var customBindingHandlerErrorHandler = new CustomBindingHandlerErrorHandler(ko); //this must be defined before any custom bindings are loaded;
customBindingHandlerErrorHandler.attach(ko); //call this before ko.applyBindings is called.
ko.applyBindings(viewModel);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment