Back to articles

Knockout.js components. What they are and what they are not

What are components?

Some time ago, Knockout.js team released a new feature — components. This feature allows developers to build some custom components that will have their own view and logic. Registration of components looks almost like binding registration.

ko.components.register('mywidget', {
viewModel: function(params) {
//Define view model here
this.title = ko.observable("Hello from component!!!");
},
template: '<div data-bind="text: title"></div>'
});

The example below looks very similar to the definition of user control in technologies like WPF/Silverlight or even WinForms. We have a template to define the view of the element and a view model to define logic.

Most interesting (for me personally) is the usage of these components as custom elements — custom HTML tags. after registration of my widget in the previous example you can write the following in your HTML code:

<mywidget></mywidget>

Brief description (skip it if you are already familiar with components)

And this HTML tag will be replaced with the template of the component with the applied component view model.

The template can be defined in the following way:

template: { element: 'my-component-template'}

Element content (only children of the element) will be cloned to a place where you apply your custom element

template: { element: getElementById('...') }
template: '<div data-bind="text: title"></div>'
template: { require: 'text!some-template.html' }

Require.js or any other AMD module loader can be used. See https://github.com/amdjs/amdjs-api/blob/master/AMD.md for details.

For view-model configuration, you can use the following:

viewModel: function(params) {
//Define view model here
this.title = ko.observable("Hello from component!!!");
}
viewModel: { instance: viewModelInstance }
viewModel: {
createViewModel: function(params, componentInfo) {
return new ViewModel(params);
}
}

Here we have an additional parameter componentInfo. This parameter allows us to get access to our custom element with componentInfo.element. But unfortunately, we can’t get access to this element before the template is applied and to analyze it, as it was initially added to the document. I’ll describe why I’ve said unfortunately a little later.

viewModel: { require: 'some/module/name' }
define(['knockout'], function(ko) {
return {
viewModel: function(params) {
this.title = ko.observable("Hello from component!!!");
},
template: '<div data-bind="text: title"></div>'
};
});

And register it with

ko.components.register('my-component', { require: 'some/module' });

What components are not?

Let’s assume we would like to build a component for a bootstrap button with a popover. And we would like to open this popover when the button is clicked and when another button inside the popover is clicked we would like to call some handler in the view model. Something like a button with confirmation. And we would like to add a custom confirmation template with elements bound to the view of the model.

It would be nice to have a component with the following syntax

<popover text="Donate"
data-bind="command: makeDonation"
title="Enter amount of donation">
<input class='form-control text-right' type='text'
data-bind='value: donationAmount' />
</popover>

But unfortunately, it’s not possible. There is no way to read HTML content of the component applied as a custom HTML tag, because everything view-model factory, view-model constructor, and all other functions are called when the template is applied to the component and the template are a required parameter.
Thus, you can’t build custom controls with templates inside. The only one possible option is to specify the template id as a parameter of your custom control.

<template id='donate-template'>
<input class='form-control text-right' type='text'
data-bind='value: donationAmount' />
</template>

<popover text="Donate"
title="Enter amount of donation"
data-bind="command: makeDonation"
template="donate-template"></popover>

Or use usual binding instead of component to specify template control

<div class="btn btn-xs btn-primary">
<div data-bind="popover: {title:'Enter ammount', command: makeDonation}"
class="hidden">
<input class='form-control text-right' type='text'
data-bind='value: donationAmount' />
</div>
Donate
</div>

More or less equivalent code but imagine how useful this “inline templating” can be for controls like this http://grid.tesseris.com/Home/Documentation#!/General/general

Let’s hope for future versions…

It also could be interesting to You

About reinvention of a wheel, Agile and Linux way

How often in software development do you think about rewriting some 3rd party module from scratch? The popular vision of the problem says that we should not reinvent a wheel and use existing paid or…

“Why do people refuse to use WebRTC? Is it really its quality question?”

As a CEO who has 15+ years of software development, I take part very often in the first call with our clients. Very often, I can hear something like “We should like to move away…