How do Angular js and Knockout

From Knockout to AngularJS

Table of Contents
  1. introduction
  2. Focus
    2.1. Bindings
    2.2. Templating
    2.3. Modular code
    2.4. Routing
  3. Conclusion
  4. Downloads & Links

The JavaScript framework Knockout.js clearly focuses on one task. This is the provision of an MVVM engine. Additional functionalities such as modular code or client-side routing have to be added by further libraries. Numerous additional JavaScript libraries have to be added gradually, especially for larger single-page applications. Anyone who has relied on Knockout.js as the technology for a SPA (single-page application) can sometimes be a little disillusioned by the Zechnology Zoo.

In contrast to this is AngularJS. This framework offers a much wider range of functions. There are many functionalities that can be used for a homogeneous SPA architecture. AngularJS is preparing to become "market leader" for SPAs and to maintain this position thanks to the support of Google.

In this session (and with the handout you have here) we will compare Knockout and AngularJS. Based selected focus areas the respective advantages and weaknesses should be worked out and demonstrated with code examples. Johannes Hoppe always sheds light on the question of whether and how a switch from knockout to Angular JS is or would be sensible and feasible.

A SPA architecture is always about bringing as much control flow and rendering logic as possible from the web server to the browser. Ideally, the web server only delivers a single HTML document, which then takes control. In principle, a wide variety of design patterns can be applied to this individual HTML document. In practice, however, it has been shown that the MVC (Model-View-Controller) pattern is the preferred implementation. This design pattern has become the standard on the server. It is not a bad idea to carry over the tried and tested to the browser. Most SPA frameworks also offer an MVVM (Model-View-ViewModel) engine. These two principles complement each other well. Due to their great overlap in terms of content, it makes sense to lump both principles together and stir them vigorously. This is exactly how the makers of AngularJS see it, who pragmatically call their framework a MVW (Model-View-Whatever (works for you)) framework.

On the premise that we are in the context of a WMD Move application, it makes sense to select the following focal points as comparison items:

  1. Bindings
  2. Templating
  3. Modular code
  4. Routing

It remains a bit of a dilemma. Knockout.js and AngularJS are actually not comparable with each other. Like apples and pears, both frameworks have a different focus. AngularJS claims to be a universal JavaScript framework for SPAs, but Knockout is limited to providing an MVVM engine. For a fair comparison, e.g. Durandal and AngularJS could be measured against each other.

2.1. Bindings

The following example is based on a simple form that changes the content of a yellow note when the value changes:

In the days before MVVM, simple UI topics were sometimes very complex. The following source code demonstrates how, for example, the sticky note only has to be processed with jQuery.

demo

You can see that the individual HTML elements are cumbersome to access. MVVM offers a clear simplification here.

The main task of an MVVM engine is the View (which in our case is pure HTML) as elegantly as possible with the so-called ViewModel connect to. The ViewModel can be seen as a special controller. On the one hand, it provides data of the business logic or the model and also represents methods for these. The "ViewModel" intermediate layer separates the view and model from one another. It is now irrelevant where and how the actual model exists. The ViewModel "hides it" and creates a standardized view of it. Often it will be the case that the actual model is only really tangible on the server. To do this, the ViewModel then directs all operations via AJAX to the server.

knockout

This connection between View and ViewModel is called Binding, it usually goes both ways. If the ViewModel changes, the view is updated. If the value of an interaction element changes (e.g. here an input field), the ViewModel is also changed. This is exactly what happens in the following example, which is implemented with Knockout.js.

demo

With knockout, objects of the type are used for the two-way bindings. These implement (as the name suggests) the observer pattern. Correspondingly, the bindings to HTML elements are specified with the data attribute.

Angular

In AngularJS, simple scenarios are quite similar. What is refreshing, however, is that even less JavaScript needs to be written. This is made possible by so-called "directives". Directives are markers in the HTML that give instructions to the AngularJS HTML compiler (). This enables a very declarative description of the application.

demo
Demo2

In this example we find the directives which an application automatically provides ("auto-bootstrap"), which executes code (eval) and here, for example, sets a model quick-and-dirty and which the view and the model via two- Way binding connects.

It is noticeable that the model does not have to implement any observables. This is where Angular differs fundamentally from Knockout. Incidentally, the AngularJS documentation does not differentiate between "Model" and "ViewModel". One speaks more of the $ scope that holds references to one or any model. (e.g. $ scope.model) This is consistent, otherwise you would not have WMD but a MVVM Application. But one can also easily argue that the $ scope is clearly a ViewModel in the sense of MVVM.

Best practices:

The above example is a little messy, because we manipulate the $ scope directly in the view (for the sake of brevity). In this case we add a newly created property "model" to the. However, the scope should not be changed in the view, this is the responsibility of the controller. You can find a clean example in the second demo.

Note:
In the world of Angular there is no rule about modeling. The model is the "business logic" of the application that is beyond Angular's control. The $ scope is therefore not understood as "the business logic". The scope always only contains references to the model, which can consist of one or more objects:

By using this, we are disrupting the well-known AngularJS recommendation that one point must always be used at one point. (E.g. here So there are no surprises if with primitive types (like the string whose values ​​are copied) the model cannot apparently be changed with two-way bindings (see "Scope inheritance" in Understanding Scopes)

Is it possible to change?

An exchange of the MVVM engines is possible, since AngularJS basically covers the functional scope of Knockout.js and also expands it. Both engines use the DOM as a view, so that not everything has to be rewritten. The directives can help minimize the number of lines of code. There will definitely be stumbling blocks due to the fact that an AngularJS model is not "observable". This principle is called "dirty checking". Behind the scenes, AngularJS puts a so-called list for each binding. (Info on $ watch and $ digest) The watches are used to detect changes. In addition, there are standard functions such as or, which take dirty checking into account. In most of them Changes are recognized correctly in cases, but intensive testing is necessary to be really sure.

2.2. Templating

In every template language there is an option to avoid repetitive code. In this example, it is advisable to outsource the yellow sticky note - so that it can be used several times.

knockout

In Knockout this can be done directly via the binding.

demo

Angular

An equivalent functionality can be found in AngularJs with Custom directives to implement. In addition to the directives already mentioned (e.g.), you can specify your own directives with a simple command. There are hardly any limits to the freedom of design, a self-created directive can be applied to a DOM element, DOM attribute, a CSS class name or a comment. The following example uses a DOM element "sticky-note" because the resulting source text is so easy to read.

The directive replaces all elements called "sticky-note" and uses the markup from here. The content of "angular.tmpl.html" corresponds to the code previously used:

demo

Is it possible to change?

The change from Knockout to Angular should be relatively unproblematic with regard to existing co-templates. It is of course imperative to port existing co-bindings here as well. However, the principle of the templates is comparable in both frameworks and, with the corresponding manual effort, can be transferred without any surprises. Compared to the templates from Knockout, Angular directives are much more flexible, but still easy and understandable to use. The good documentation on the topic makes the switch easier.

2.3. Modular code

Nowadays it should be standard to structure JavaScript code in a modular way. A module encapsulates functionality on the one hand and discloses its dependencies on the other. Modules make it possible to achieve a loose coupling between functionalities - which is generally considered to be clean code. A module loader can then resolve these dependencies, provided the code implements the appropriate format. The AMD (Asynchronous Module Definition) format has prevailed in the browser, its reference implementation is require.js. Other JavaScript loaders include YepNope, $ script.js, LABjs, headjs, the loader from the Dojo Toolkit or curl.js. Besides AMD, which one for scenarios in the browser is designed (asynchronous reloading), CommonJS is an alternative format, which is mainly used by Node.js on the server is used.

knockout

Knockout does not require the use of modules. The developer is completely free to structure the ViewModel or the initialization code according to his own ideas. (Unfortunately, this leads to a lot of KO code that is not structured at all.) However, if knockout is executed at a time when either the CommonJs or an AMD loader (such as require.js) has been executed, so Knockout presents itself as corresponding module. (KO uses a variation of the UMD (Universal Module Definition) pattern)

The following example shows how a ViewModel specifies Knockout as a dependency. This works without any special adjustments:

demo

Unlike AngularJS, Knockout never takes the lead. It sees itself as one of many modules of an application and leaves it to the developer to design a more or less modular architecture.

AMD is just a little bit of dependency injection

The AMD pattern primarily addresses the isolation of code and the reloading of code as a defined dependency. However, AMD does not specify what the content of a module is. There is the same freedom as with all other JavaScript objects. For example, you can define simple key-value pairs or complex objects as the "type" of the module. However, this is also the crux of AMD: Modules are any kind of JavaScript object. It is only clear after studying the relevant documentation (or the source text) whether

  • a) the returned module has properties that are to be used directly (i.e. the module behaves like a singleton),
  • b) it should be called as a function or
  • c) it is e.g. a constructive function which is to be called with the keyword.

AMD / Require.js can be seen as a puristic service locator. Exchanging dependencies for test purposes is possible (as described here, for example), but a bit cumbersome. Working with AMD can be described as a "Dependency Injection" light. After all, you don't always get instantiated dependencies, you have to instantiate the modules or properties / parts of the module yourself. There are simply no strict specifications in AMD format that would allow the module or parts of the module to be instantiated directly by the module loader.

Angular

Modular AngularJS code was already used in the previous examples. AngularJS uses its own module format, in which Angular modules are generated by the command. You have to make sure that two completely different concepts meet here:
.

AMD / require.js regulates the (asynchronous) loading of JavaScript code, which is available in AMD format. This happens mainly once at the start of the application. A module is only loaded once, even if it is used more than once. This means that every AMD module is effectively a singleton.

.

AngularJS modules use the various methods of the $ provide-service to configure the $ injector, which can assemble a finished object at runtime. For this purpose, the $ injector can instantiate types, execute methods and also load additional modules. The finished object contains one or more Services. Services are always singletons.

In AMD you get a module. There are no further requirements.

At Angular you get a module that contains several named Services may contain. With these conventions to be adhered to, one can achieve a fully-fledged "Dependency Injecton" - especially in terms of testability. The difference between the various $ provide methods (Service, Factory & Provider) is described in detail in the Developer Guide. In short, the following three providers each create a service that has a HelloWorld method.

If you want to use the functionality of the services, you can accept this service as a function parameter:

demo

Before calling the controller, the $ injector checks the signature of the function ("Reflection" via .toString ()) and then "injected" the desired service. It is therefore very important to pay attention to the exact spelling of the function parameters. AMD does not have a comparable comfortable use.

Is it possible to change?

As has been shown, AMD modules and Angular modules are two concepts that have different priorities. With a few small adjustments, both concepts can be combined. In this way, AMD code can be transferred from the knockout world to the Angular world. This is strongly recommended because it is very impractical or error-prone to combine synchronous and asynchronous code with one another. (You either rely entirely on AMD or not at all!)

However, you can no longer use the directive because it is already evaluated during the browser event in the course of the automatic bootstrapping. At this point in time, the asynchronous modules have not yet been loaded.

Therefore, bootstrapping should only be started when require.js has loaded all modules. The standard module is ideal for this. It is now possible to enclose an Angular module with an AMD module without any problems:

demo

2.4. Routing

knockout

One more principle is still missing, which is essential for a SPA: Client-side routing.
"Routing" means that the application can switch between views and update the browser history in the process. This makes it possible to use the browser's "Back" and "Forward" buttons as usual. The routing should also ensure that you can jump to any view by calling up the corresponding URL in the browser. If the routing is well implemented, it is no longer clear to the user whether it is a "classic" application with several HTML pages or a SPA (although the advantages of single-page, such as fast loading times, should of course be retained) .

Knockout does not offer routing out of the box. It makes sense to either rely on Durandal or to implement the principle yourself with one of the many routing libraries available. Examples include Sammy.js, FinchJS or Director.

How a solid routing with knockout can be implemented is part of the following documentation.

It boils down to developing a module that holds the state (here the "appState.js" file with Sammy.js) and thus encapsulates the routing. This routing file can then instruct another module to load a view and a view model, depending on the route, and to execute them with knockout. That module is simply called "app", you could also call this module "controller". This is what this central routing file looks like in this example:

(see appState.js)

Angular

The biggest problem with the present knockout example is the unnecessarily high complexity and lack of standardization. It would be much more advantageous if the existing MVVM framework already offered all the necessary functionalities. This is exactly what AngularJS offers.

.

One could argue that Durandal (the best known SPA framework for KO) provides the necessary standardization, but Durandal is no longer being developed in favor of AngularJS!

Angular relies on the $ routeProvider, which monitors the history and, if necessary, loads a template and calls the appropriate controller.The usage is easy to see. You define a directive

configured according to:

and defines a suitable list template (templates / list.html):

demo

3. Conclusion

It is difficult to come to a conclusion. It is obvious that Knockout alone offers very little functionality. But it is quite possible that you already have an excellent architecture with knockout as an MVVM engine. In this case you should stick with Knockout, because Knockout is constantly being developed and maintained. I would recently advise against an "upgrade" to Durandal, since Durandal and AngularJS are not expected to introduce any new innovations due to the convergence of Durandal.

If you do not have an excellent architecture ("historically grown"), then it makes sense to switch to AngularJS. The makers of AngularJS have already made many architectural decisions for you. Due to the modular architecture and the good testability, the new generation of "bad code" is sufficiently avoided!

4. Downloads & Links

You can find this document at: http://bit.ly/Ko2NgDocs
You can find the presentation here: http://bit.ly/Ko2NgSlides

The full-length examples are available on Github:
https://github.com/JohannesHoppe/FromKnockout2Angular
(Folder: / Slides / examples /)


© 2014, Johannes Hoppe