Monday, September 03, 2012

knockout.composite: The Basics

*UPDATE* Tribe is here! Check out http://tribejs.com/ for guides and API reference.

This is part 2 of a multi-part post. From here on in, it’s assumed that you have a basic knowledge of the data binding and observability features of knockout.js. Head over to http://learn.knockoutjs.com/ and run through a tutorial or two to get up to speed, it’ll only take a minute or two.

If you’re feeling confident or short on time, you can probably skip to part 4 and get stuck into a more meaty example.

Part 1: Introducing knockout.composite
Part 2: The Basics
Part 3: Getting Around
Part 4: Putting it All Together
Part 5: Unit Testing: Model Citizens 
Part 6: Integration Testing: Playing Nicely Together
Part 7: Functional Testing: Automated UI Tests, Anywhere, Any Time
Part 8: Pack Your App and Make it Fly
Part 9: Parting Thoughts and Future Directions

 

knockout.composite weighs in at 19.2kb minified and 6.4kb minified and gzipped. It currently depends on jQuery 1.7.1 and knockout.js 2.1.0, though I plan to remove the dependency on jQuery in a future version. For back button support, the jQuery hashchange plugin is required.

The code in this post is based on the examples available in the source code and in the live examples. It is strongly recommended that you use Chrome to work through the examples. It is the only browser that fully supports the sourceURL tag for dynamically loaded scripts. Crack open the developer console (F12) and show the sources – full breakpoint support in all of your models, all neatly arranged with the correct names. Nice!

Creating Panes (demo)

As described in part 1, a pane consists of three resources – a HTML template, a JavaScript model and a CSS stylesheet. When a pane is created, the framework renders the template and CSS, creates an instance of the model and binds it to the template.

Creating a pane in markup is as simple as invoking the pane binding handler:

<body data-bind="pane: 'helloWorld', data: { message: 'A message to pass...' }">

The pane handler also accepts an object containing the pane options. This format allows passing additional options to the pane creation process. We’ll cover some of those options later.

<body data-bind="pane: { path: 'helloWorld', data: { message: 'A message to pass...' } }">

We can also dynamically inject panes into the DOM using a JavaScript utility method:

ko.composite.utils.addPane(document.body, {
    path: 'helloWorld',
    data: { message: 'A message to pass...' }
}, pane);

Basic Communication

Publishing and Subscribing to Messages on the Event Bus (demo)

Publish / subscribe is the primary mechanism for communication between panes. This promotes loose coupling between application components and reuse of panes. The knockout.composite codebase contains a fully functional publish / subscribe mechanism, though future versions will allow adapters to be written for other pubsub libraries.

An instance of the pubsub engine is passed to each pane, and publishing and subscribing is as simple as calling the appropriate methods:

pubsub.publish('message', self.value());
pubsub.subscribe('message', function (value) {
    self.value(value);
});

It’s important to be aware that the current version of knockout.composite contains an automatic clean up mechanism that cleans up normal subscriptions each time the pane navigates (covered next post). If you require subscriptions to persist longer than this (such as in layout components that are always visible), use the subscribePersistent method. You will need to unsubscribe these yourself, usually in the dispose lifecycle method (see below), or memory leaks will occur.

This will all change in the next version where subscriptions will be automatically removed when the pane’s corresponding element in the DOM is destroyed.

Sharing Observables (demo)

A powerful technique for keeping data synchronised across your application (particularly lists) is to pass a single instance of an observable to each consumer pane. knockout.js keeps the UI, or any subscribers to the observable up to date.

Panes, Models and Lifecycles (demo)

Lifecycle. Sometimes makes me think of WebForms. It’s far, far simpler, I promise.

Each pane and it’s associated DOM element has a life of it’s own, from creation through rendering to destruction. There are four predefined events that we can hook in to by simply defining some functions on our models:

  • initialise – this function is called immediately after the model is constructed. If we return a jQuery deferred object from this function (such as one returned by $.ajax), rendering will wait until the deferred completes, allowing us to load data before the pane is rendered.
  • rendered – called when the current pane has completed rendering and data binding the model to the template. This does not include any panes that are nested within the current pane.
  • childrenRendered – called when all panes in the current rendering cycle (children and siblings) have completed rendering and binding.
  • dispose – called when the pane’s associated DOM element has been destroyed. This is typically where you unsubscribe from any persistent event bus subscriptions or global DOM events.

The next version of knockout.composite will provide a configurable and extensible pane lifecycle.

Next…

Coming up, we’ll have a look at navigation, including memory management, transitions and bookmarking / back button support.

0 Comments:

Post a Comment

<< Home