X

Geertjan's Blog

  • January 18, 2016

Components in Angular 2, Knockout, and Oracle JET

Geertjan Wielenga
Product Manager

When you're learning something new, you sometimes are reminded of other things you've learned, in other domains. For example, when I look at Angular 2 components, such as this one for example...


...then I am reminded of the syntax used by Knockout for expressing components:

Obviously, there are differences. For example, the Angular 2 component is expressed in TypeScript, while the Knockout component is expressed in JavaScript, though TypeScript could also be used (and also see this).

Once a component is declared, this is how it is used in Angular 2:

<greetingForm>Loading...</greetingForm>

There is a similar syntax for using Knockout components, though this is the more recommended approach:

<div data-bind="component: {name: 'greetingForm', 
params: {first: 'Planet', last: 'World' }}">
</div>

You could initialize the variables in various ways, above you see how it could be done via the component binding in Knockout. You could also use the "placeholder" attribute of the "input" elements or you could initialize the variables directly in your code.

Also, in both cases, instead of writing the HTML template directly in the JavaScript/TypeScript code, you can reference an HTML file, where you could have more complex HTML content, while making it possible to use HTML editors and similar tools.

In the case of Angular 2, there's more going on, since SystemJS is used, giving you a global System object, with a number of static methods, so that the usage of the "greetingForm" element above is slightly more involved, i.e., this is how the "greetingForm.js" is loaded into the "index.html" file, in order for the "greetingForm" component to be used:

<greetingForm>Loading...</greetingForm>
<script>
System.config({
transpiler: 'typescript',
typescriptOptions: {emitDecoratorMetadata: true},
packages: {app: {defaultExtension: 'ts'}}
});
System.import('app/greetingForm').then(null, console.error.bind(console));;
</script>

Out of the box, Knockout doesn't have a module loader. That's because Knockout is a library, while Angular 2 is a framework. Knockout is ONLY focused on two-way data-binding, nothing else. It doesn't come with anything other than that, e.g., no module loader. (And for that reason, a "shoot out" between Angular and Knockout doesn't make any sense at all.) If a module loader is needed, a popular approach is to use RequireJS together with Knockout. Here, for example, is a Knockout component, expressed as a RequireJS module, making use of the requirejs/text protocol to load the HTML template:

And here is the registration of one or more Knockout components, in another RequireJS module, named "initComponents.js":

define([
'knockout'
],
function (ko) {
return function initComponents() {
ko.components.register("greetingForm", {require: 'js/modules/greetingForm'});
};
});

And then, in the end, in your "require" block, you'd have something like this:

ko.applyBindings(new initComponents());

Then RequireJS would do the module loading for you in your Knockout applications, comparable to what SystemJS does for Angular 2.

When you're using Knockout and RequireJS, you should really consider using Oracle JET, in this context, because it provides some very useful features:

  1. You do not need to register your components.
  2. You do not need to reference your HTML templates.

The above are two interesting approaches that Angular 2 could benefit from, as well.

Firstly, this is how your Knockout component is expressed as a RequireJS module in Oracle JET, take note of the fact that there is no reference to an HTML template here:

Secondly, Oracle JET uses convention-over-configuration to locate modules. Notice below that the JavaScript file is in "js/viewModels", while its view template is in "js/views". And the two files have the same name:

That is all that is needed to be able to use the "greetingForm" module in my Oracle JET application, like this, which means that Oracle JET will look in "js/viewModels" for "greetingForm.js" and in "js/views" for "greetingForm.html":

<div data-bind="ojModule:{name:'greetingForm'}"></div>

It is easy to override those predefined locations, by the way. But it's really cool that there is such a thing, i.e., "js/viewModels" for business logic and "js/Views" for HTML templates.

Oracle JET will put those two files together and treat them as a module and load them into the application, using Knockout and RequireJS under the hood.

In this blog entry, I've compared the syntax and usage of components in Angular 2, Knockout, and Oracle JET. None of the approaches are necessarily better or worse, it's simply interesting to put them next to each other like this and compare them with each other.

Join the discussion

Comments ( 1 )
  • alex Monday, January 18, 2016

    I'd say it's always important to compare solutions and keep things in perspective.

    Looking at your examples I so miss the elegance of Dart...


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.