Knockout catching errors
http://brianmhunt.github.io/articles/knockout-catching-errors/
by bmh_ca
HTML
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-min.js"></script>
<div id='X'>
<h1>Sample of errors</h1>
<h2>A binding that does not exist</h2>
<div data-bind='doesnotexist: obs'></div>
<h2>A binding init throws an error</h2>
<div data-bind='throws: obs'></div>
<h2>When no error occurs</h2>
<div data-bind='text: obs'></div>
</div>
CSS
/* [data-bind] */.binding-error {
border: 4px dashed red;
background-color: #F88;
}
/* [data-bind] */.binding-error:after {
font-size: 16px;
color: red;
background-color: white;
padding: 2px 0.5em;
border: 1px solid gray;
font-style: italic;
content: "This item may not be working as expected.";
}
CoffeeScript
ErrorHandlingBindingProvider = ->
# The original binding handler still does the real work; you
# can use your own or an alternative such as knockout-secure-binding
# here though.
orig = new ko.bindingProvider()
# We also pass through the `nodeHasBindings` call.
@nodeHasBindings = orig.nodeHasBindings
# This returns a map of binding names to respective handlers, given a node
# and the bindingContext.
@getBindings = (node, bindingContext) ->
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 err
# When there is an error, throw out some useful context.
console.error("Binding error: ", err.message, "\nNode:", node,
"\nContext:", ko.contextFor(node))
$(node).addClass("binding-error")
# Pass null straight up.
if bindings_map == null then return null
# If there isn't at least one good binding, print some helpful
# debugging information.
unless _(bindings_map).keys().any((m) -> _.has(ko.bindingHandlers, m))
console.error("No bindings found:", bindings_map, "Node:", node)
$(node).addClass("binding-error")
return null
return bindings_map
return
# Replace the default Knockout binding provider.
ko.bindingProvider.instance = new ErrorHandlingBindingProvider()
# The `isolate_call` function wraps `binding.init` and `binding.update`
isolate_call = (binding, fn_name, key) ->
# binding is the binding object
# fn_name is one of `init` or `update`
# key is the name of the binding
fn = binding[fn_name]
unless fn then return undefined
unless _.isFunction(fn)
console.error("%cERROR%c Binding #{key}.#{fn_name} is not a function",
"color:red", "color:black", binding, fn_name, fn)
return undefined
wrapped_fn = (args...) ->
try
# Pass-through the call to the original handler.
return fn.apply(binding,...