Establishing Bindings

At the heart of Coherent is the process of binding values in your data model to views in your UI. But this can be rather confusing, since it’s an entirely different style from traditional web development.

There are two ways to establish bindings and each has its place. The first is straight programatic binding via calling Bindable#bindNameToObjectWithKeyPath. The second method relies on the parameters dictionary passed to the constructor of a Bindable object.

Programatic Bindings

The programatic method of establishing bindings is probably the simplest to understand. Call bindNameToObjectWithKeyPath on a view or controller to establish a binding. You’ll need to provide values for the name of the binding you wish to create, the object you wish to which you want to bind, and the keypath on the object that will be bound.

For example:

var view= new coherent.View('node-id');
var obj= new coherent.KVO();

view.bindNameToObjectWithKeyPath('text', obj, 'title');

This works pretty much as you’d expect: The implementation of bindNameToObjectWithKeyPath observes the key path title on obj and calls the setText method on view whenever a change occurs. The view could update the title property of obj, but that’s a topic for another time.

There’s nothing wrong with creating bindings programatically, but I prefer to establish them when I create the view.

Declarative Bindings

The constructor of all Bindable objects accepts a dictionary containing additional parameters for the newly created object. Bindings are a special case of parameters, any keys that end in Binding are assumed to be a binding definition. So the previous example can be rewritten as:

var view= new coherent.View('node-id', {
                    textBinding: 'obj.title'
                });

But wait! There’s more! You can also specify options for the binding by changing the string key path into a dictionary1:

var view= new coherent.View('node-id', {
                    textBinding: {
                        keypath: 'obj.title',
                        transformer: 'truncated',
                        nullValuePlaceholder: 'No Title',
                        noSelectionPlaceholder: 'No Image',
                        multipleValuesPlaceholder: 'Multiple Images'
                    }
                });

All entries in the dictionary are optional — except the keypath of course.

The placeholder entries provide values for the binding when the value of the key path represents a special marker value: null value (coherent.NullValueMarkerType), no selection (coherent.NoSelectionMarkerType), or multiple values (coherent.MultipleValuesMarkerType). These are typically only used when binding to the selection property of an ObjectController or ArrayController. The value of these entries may be of any type that will make sense to the view; no value need be provided if the default value specified by the view is acceptable.

The transformer entry is either the registered name of a ValueTransformer or an instance of a ValueTransformer. For example, if you’re not comfortable with the default truncating transformer, which truncates at 50 characters, you could create your own:

var view= new coherent.View('node-id', {
                    textBinding: {
                        keypath: 'obj.title',
                        transformer: new coherent.transformer.Truncate(20)
                    }
                });

Sometimes you need to transform model values somewhat ad hoc, like when converting a model value into a class name. In those cases, you can specify a transformation method directly in the binding definition:

var view= new coherent.View('node-id', {
                    textBinding: {
                        keypath: 'obj.title',
                        transformedValue: function(value)
                        {
                            if ('string'!==typeof(value))
                                return value;
                            return value.replace(/[^aeiou]+/gi, '');
                        }
                    }
                });

All About Context

There’s one important thing missing in the code samples from the previous section: the object value for bindNameToObjectWithKeyPath. The declarative method takes the value of object from the currently active context. There is always an active context. In many cases, it’s the global context, but there are also contexts for NIBs and the internal structure of views.

First, let’s make the example work with the global context:

var obj= new coherent.KVO();
coherent.registerModelWithName(obj, 'obj');

var view= new coherent.View('node-id', {
                    textBinding: 'obj.title'
                });

The call to registerModelWithName wires up an object to the global context with a given name. Behind the scenes this is nothing more than a call to setValueForKey(obj, 'obj') on the global context.

NIB Context

The global context isn’t usually where bindings will be evaluated, however. Most bindings get evaluated in the context of a NIB. A NIB represents a collection of views and controllers for a specific chunk of your application. Each entry in the NIB is exposed in the active context.

In the following example, the NIB context will contain entries for gallery, data, and controller:

NIB({

    'gallery': VIEW(NIB.asset('gallery.html'), {
                    ':root': coherent.View({
                                visibleBinding: 'controller.arrangedObjects.@count'
                            }),
                    'img': coherent.Image({
                                srcBinding: 'controller.selection.href'
                            }),
                    'p': coherent.View({
                                textBinding: 'controller.selection.caption'
                            }),
                    'a.next': coherent.Anchor({
                                enabledBinding: 'controller.canSelectNext',
                                target: 'controller',
                                action: 'selectNext'
                            }),
                    'a.prev': coherent.Anchor({
                                enabledBinding: 'controller.canSelectPrevious',
                                target: 'controller',
                                action: 'selectPrevious'
                            })
                }),

    'data': NIB.asset('gallery.json'),

    'controller': coherent.ArrayController({
                    contentBinding: 'data.photos'
                }),

    'owner': {
        view: 'gallery'
    }

});

In addition to the entries defined in the NIB, there are a number of special entries that represent objects external to the context: owner and application. The owner entry is usually an instance of coherent.ViewController and application is the shared coherent.Application instance. You can connect views and data objects to the owner or application by specifying the linkages in a dictionary associated with those keys.

View Context

In addition to NIB contexts, if you create subclasses of coherent.View, you’ll probably encounter View contexts. During initialisation of a view and its children, the context is set to the containing view. This allows child views to access properties of their parent.

For example:

views.MyView= Class.create(coherent.View, {

    title: coherent.View({
                textBinding: 'representedObject.title'
            })

});

Then later, you can do the following:

var view= new views.MyView('my-view-id');
var obj= new coherent.KVO();
view.setValueForKey(obj, 'representedObject');

Template Context

The final context you’re likely to encounter is the template context created for each item in coherent.CollectionView#newItemForRepresentedObject. This is something of a special case, because unlike the other contexts, the template context contains all the entries from the context used to create the CollectionView plus an entry (representedObject) for the current item in the collection.


  1. Actually, you may pass a dictionary as the keypath parameter to bindNameToObjectWithKeyPath. That’s exactly what’s happening under the covers with the declarative syntax. 

Performance Tuning

So I’m deep in the middle of some performance tuning work for the release of Coherent 3.0. Everyone knows that optimising Javascript is a bit of a black art, but I almost think I’m doing something wrong.

Here are the performance metrics for Coherent 2.0 running on IE 8 (in a VM):

      direct:  5.688s for 50 iterations  (Avg: 113.755ms)
     derived:  6.086s for 50 iterations  (Avg: 121.720ms)
     methods:  9.793s for 50 iterations  (Avg: 195.860ms)
      notify: 10.946s for 50 iterations  (Avg: 218.910ms)
notify class:  6.785s for 500 iterations (Avg:  13.570ms)

Coherent’s never been a stellar performer in Internet Explorer. That’s something of an understatement, but I’ve always felt the blame rested largely on IE’s craptacular Javascript engine.

Now, I’m not so sure.

Below are the same run times for the same performance tests, but this time the tests are running against the new optimised code in Coherent 3.0:

      direct:  4.309s for 50 iterations  (Avg:  86.170ms)
     derived:  0.625s for 50 iterations  (Avg:  12.500ms)
     methods:  0.910s for 50 iterations  (Avg:  18.205ms)
      notify:  8.356s for 50 iterations  (Avg: 167.110ms)
notify class:  4.547s for 500 iterations (Avg:   9.094ms)

Clearly, I’m doing something right. All the unit tests still pass. And I’m getting a 9.7× speed improvement for derived property access — that’s when accessing properties of a class derived from coherent.KVO, a 10.7× speed improvement for calling getter and setter methods, and significant 1.3× to 1.5× improvement for the other operations.

The same code running in other browsers (Safari 4.0, WebKit nightly, Firefox 3.5 and Firefox 3.6) all see speed improvements between 1.3× and 2.0×, but nothing nearly as dramatic as IE.

Below are the performance numbers from WebKit nightly running Coherent 2.0:

      direct: 304.750ms for 100 iterations  (Avg: 3.047ms)
     derived: 324.750ms for 100 iterations  (Avg: 3.248ms)
     methods: 286.250ms for 100 iterations  (Avg: 2.862ms)
      notify: 742.750ms for 100 iterations  (Avg: 7.428ms)
notify class: 257.750ms for 1000 iterations (Avg: 0.258ms)

And WebKit nightly running Coherent 3.0:

      direct: 195.500ms for 100 iterations  (Avg: 1.955ms)
     derived: 204.000ms for 100 iterations  (Avg: 2.040ms)
     methods: 213.500ms for 100 iterations  (Avg: 2.135ms)
      notify: 468.750ms for 100 iterations  (Avg: 4.688ms)
notify class: 162.500ms for 1000 iterations (Avg: 0.163ms)

This is just the result of changes to the core methods of coherent.KVO and coherent.KeyInfo. Of course, there’s still more performance tuning to come. But I don’t think I’ll have anything this dramatic to report again…

Resurrected Old Content

As part of getting the new coherentjs.org Web site ready, I dragged out a back up of the original content. The import for Wordpress went relatively smoothly, but it’s pretty clear this content either needs to be cleaned up or rewritten.

The biggest problem is that a lot of it is simply not accurate any more. However, I’m going to leave it there for historical interest — I’m a bit surprised reading about my first efforts back in 2005. Maybe it will interest other developers too.

Probably the most important feature of the new site is the API documentation. This is greatly expanded from the original documentation from Coherent 1.0. And it will only get better as I continue to review the code and add doc comments.

Declarative Syntax for Child Widgets

One of my goals for Coherent 1.1 is the option of using a declarative syntax to set up child widgets. This would greatly simplify the average init method and make the code a bit clearer and easier to understand. Read More

Favourable Reception for Coherent

I recently announced the upcoming release of Coherent 1.0 and I’ve been very pleased by the positive reception the library has received.

Lots of folks have come out of the woodwork to either say they’ve been looking for something like this for ages. I guess there are more fans of the Apple development model than I thought. And obviously a lot of us build Web applications either for a living or in our spare time.

As I’ve been working on some features I’ve planned for 1.1, I’ve run across some strange problems with selectors which seems to tie into John Resig’s recent blog post about selectors.

Selectors and Bindings

One of the routine complaints about Coherent (yes, I’m talking about you Ryan) is the use of custom attributes. It seems that some people like their HTML pure, like it was back in the old days. Read More

Coherent 1.0 Release Candidate 1

I’ve been working somewhat furiously to get Coherent ready for its first release. There’s already code in the wild on a heavy-traffic e-commerce site that is using it, but there is still a bit more testing I’d like to do. And the documentation could use another brush up.

If you’ve downloaded either the ZIP file or pulled down the SVN repository, you should probably update to get the latest bug fixes and other goodies.

Properties And Bindings

Possibly the two most important concepts in the Coherent library are properties and bindings. If you’re familiar with modern programming languages, you’ve probably run across properties before, but bindings may be new unless you’ve worked with Apple’s Cocoa library. In order to get the most out of Coherent, you’ll need to understand these two facilities. Read More

Faster than a Locomotive?

After squashing a couple nasty bugs this weekend, I got to thinking about the performance implications of my solution. In a nutshell, the solution required wrapping getter methods with code to establish the ownership link between the value and the object. This wrapping only occurs for properties that are observed or part of a dependent key relationship, but still, we’re talking an extra layer of code. Read More

New Tutorial on Writing Widgets

As the release of Coherent looms nearer, I’ve been encouraged to start writing some documentation. My first effort is up: Writing a Widget. This tutorial takes you through creating a widget using Coherent starting with raw mark up and ending with a functional (if simple) widget. Thanks to some great feedback (and encouragement) from Neil Mix of Pandora, this tutorial doesn’t suck.

Oh, and if you haven’t tried Pandora yet, what the heck are you waiting for? This is one of the coolest things I’ve run across in ages. Of course, now my wallet is going to be completely empty because I’ve been turned onto a half dozen musicians I’d never heard of before…