Knockout 3.2 introduces component loading – this is a great feature that allows you to load HTML and JS modules into your code, thus enabling you to split your code in to self contained modules.
Think of it like PartialViews in MVC but for KnockoutJS.
The first thing you need to do is ‘register’ your component, e.g. (And this is taken from the KnockoutJS documentation)
ko.components.register('like-widget', { viewModel: function(params) { // Data: value is either null, 'like', or 'dislike' this.chosenValue = params.value; // Behaviors this.like = function() { this.chosenValue('like'); }.bind(this); this.dislike = function() { this.chosenValue('dislike'); }.bind(this); }, template: '<div class="like-or-dislike" data-bind="visible: !chosenValue()">\ <button data-bind="click: like">Like it</button>\ <button data-bind="click: dislike">Dislike it</button>\ </div>\ <div class="result" data-bind="visible: chosenValue">\ You <strong data-bind="text: chosenValue"></strong> it\ </div>' });
<ul data-bind="foreach: products"> <li class="product"> <strong data-bind="text: name"></strong> <like-widget params="value: userRating"></like-widget> </li> </ul>
function Product(name, rating) { this.name = name; this.userRating = ko.observable(rating || null); } function MyViewModel() { this.products = [ new Product('Garlic bread'), new Product('Pain au chocolat'), new Product('Seagull spaghetti', 'like') // This one was already 'liked' ]; } ko.applyBindings(new MyViewModel());
define(['knockout'], function(ko) { function LikeWidgetViewModel(params) { this.chosenValue = params.value; } LikeWidgetViewModel.prototype.like = function() { this.chosenValue('like'); }; LikeWidgetViewModel.prototype.dislike = function() { this.chosenValue('dislike'); }; return LikeWidgetViewModel; });
<div class="like-or-dislike" data-bind="visible: !chosenValue()"> <button data-bind="click: like">Like it</button> <button data-bind="click: dislike">Dislike it</button> </div> <div class="result" data-bind="visible: chosenValue"> You <strong data-bind="text: chosenValue"></strong> it And this was loaded from an external file </div>
ko.components.register('like-or-dislike', {
viewModel: { require: 'files/component-like-widget' },
template: { require: 'text!files/component-like-widget.html' }
});
HTML
<ul data-bind="foreach: products"> <li class="product"> <strong data-bind="text: name"></strong> <like-or-dislike params="value: userRating"></like-or-dislike> </li> </ul> <button data-bind="click: addProduct">Add a product</button>
script
function Product(name, rating) { this.name = name; this.userRating = ko.observable(rating || null); } function MyViewModel() { this.products = ko.observableArray(); // Start empty } MyViewModel.prototype.addProduct = function() { var name = 'Product ' + (this.products().length + 1); this.products.push(new Product(name)); }; ko.applyBindings(new MyViewModel());
Now, this is pretty much was the Knockout documentation says – it works lovely on their own pages, but could I get it to work in my project? Nope. Not at all.
So, time to start digging. First thing let’s have a look at what is being brought back from the server (I Use fiddler).
The javascript file loads fine – but when it ties to load the HTML file it does;t actually try to load it, instead it tries to get a file called ‘text’
Well I guess this makes sense – after all we’ve told it to load ‘text!widget-like-component.html’