After looking around though I came upon a solution to the CSP problem in Knockout. It seems I wasn't the only one with the problem. I found that Brian Hunt created a binding provider for Knockout that is CSP compliant. The project, called Knockout Secure Binding (KSB) can be found on GitHub. It is a replacement for Knockout's built in binding provider.
KSB is not a complete replacement though. It is limited in the kind of bindings you can create. Since it can't use "new Function" to parse bindings you can't create more complex bindings such as passing parameters into functions, and only a subset of JS expressions are available. But these limitations can easily be resolved by being more mindful of your bindings.
For example, say you have a binding for a "like" button that sets a certain thing as being liked. It calls a function in your view model called "like" and passes in the thing to like and true or false.
<button data-bind="click: like("knockoutJS", true)">Like</button> <button data-bind="click: like("knockoutJS", false)">Dislike</button>
This won't work when using KSB because it doesn't support passing parameters to functions. You will need to rewrite the binding so it doesn't have to pass parameters.
<button data-bind="click: likeKnockoutJS">Like</button> <button data-bind="click: dislikeKnockoutJS">Dislike</button>
KSB supports most any logical expression including comparison and mathematical operators, so you can perform some basic binding expressions.
<button data-bind="visible: !knockoutJSLiked())">Like</button> <button data-bind="visible: knockoutJSLikeCount() > 0">Dislike</button>
Here the like button is visible only when the knockoutJSLiked() function does not return true. The dislike button is only visible when the knockoutJSLikeCount() function returns a value greater than zero.
To use KSB you just need to tell Knockout to use it as your binding provider before applying your bindings. You create an instance of a ko.secureBindingsProvider passing in the options you need and set that as the ko.bindingProvider.
let options = {
attribute: "data-bind", // default "data-sbind"
globals: window, // default {}
bindings: ko.bindingHandlers, // default ko.bindingHandlers
noVirtualElements: false // default true
};
ko.bindingProvider.instance = new ko.secureBindingsProvider(options);
ko.applyBindings(this.viewModel);
For more info on using KSB see the GitHub page.
In my app (PowerChord) I had to do some reworking to get my bindings to work with KSB. I also had to rewrite some components to make them work, which was probably a good thing because they were a bit convoluted. KSB forced me to write my components the way they should have been written instead of trying to use shortcuts with complicated bindings in my view.
Overall I'm very pleased with KSB and highly recommend it if you are writing a Chrome web app or need to implement the CSP and want to use Knockout as your binding framework (which I highly recommend).
No comments:
Post a Comment