How to log knockout.js errors?
Right now I’m working in a banking area project. It’s a web project and we are using Knockout.JS framework.
We (developers) don’t have access to the project front-end in Production Environment. On the server side we log all errors and debug information into a table in our database. Yes, we don’t have access to our project front-end but we are able to consult (and only with read privileges) our database.
So, when happens a client side error in Production Environment, it’s pretty hard to figure out what’s wrong. All we have is a printscreen from the user.
Sometimes it’s impossible to reproduce the Production Environment error because all the development environments (LOC-DEV-STA-PRE) have different data information and different code versions with different develepments in comparison with Production Environment. So, we really need to get detailed information about client-side errors.
In this post, I’m going to show you how to log only Knockout.JS errors. So, general javascript errors will not be discussed here.
First of all, we need to write an Error Handling Binding Provider. For more information about Custom Bindings Providers please read this blog post. Please be carefull because something may have changed because it’s based on an old knockout.JS version).
// Custom Error Handler. var MyErrorBindingProvider = function () { // Create a binding provider. var original = new ko.bindingProvider(); // check if the element has bindings this.nodeHasBindings = original.nodeHasBindings; //return the bindings given a node and the bindingContext this.getBindingAccessors = function (node, bindingContext) { var result = {}; // catch syntax errors when parsing binding try { result = original.getBindingAccessors(node, bindingContext); } // Something wrong happened... catch (e) { // do whatever you want. (alert, debug, console.log, send it to server, etc) if (console && console.log) { console.log("Error occurred in binding syntax: " + e.message, node); } } // catch errors when evaluating the value of a binding ko.utils.objectForEach(result, function (key, value) { result[key] = function () { var result = null; try { result = value(); } // Something wrong happened... catch (e) { // do whatever you want. (alert, debug, console.log, send it to server, etc) if (console && console.log) { console.log("Error occurred in \"" + key + "\" binding: " + e.message, node); } } return result; }; }); return result; }; };
Now that we have our Custom Binding Provider, we need to instantiate it.
Just add it in your document.ready function where you do your applyBindings and etc:
$(document).ready(function () { // my viewModel var viewModel = { firstName: ko.observable("Joao") }; // Error Subscription while observables values changed ko.bindingProvider.instance = new MyErrorBindingProvider(); // Apply Bindings. ko.applyBindings(viewModel); });
As you have read in the begining of this post, I wrote that we don’t have access to Production environment, so we sent any knockout error to server side in order to be stored in a database table and then we are able to consult it. This can be done with a simple AJAX call. Just send what it’s been showed on console.log in this example.
Finally, here you have an HTML sample to try it by yourself:
<!DOCTYPE html> <html> <head> <title>Knockout.JS Error Handling</title> <script type='text/javascript' src='https://code.jquery.com/jquery-3.1.0.min.js'></script> <script type='text/javascript' src='https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js'></script> <script type='text/javascript' src='Index.js'></script> </head> <body> <div> <p>First name: <input data-bind='textInput: firstName' /></p> <span>Hello </span><span data-bind='text: firstName'></span> </div> </body> </html>
Now, let’s see the difference between using our error handler and not using it.
For instance, if you forget to close your first data-bind statement:
<div> <p>First name: <input data-bind='textInput: firstName /></p> <span>Hello </span><span data-bind='text: firstName'></span> </div>
For instance, if you are trying to data-bind an undefined observable:
<div> <p>First name: <input data-bind='textInput: firstName' /></p> <span>Hello </span><span data-bind='text: lastName'></span> </div>
That’s it. I hope you enjoy it and have learn something.
you are using the var name “result” in 3 different contexts within the same function.