custom binding provider (data-class)

by rniemeyer

HTML

<script src="http://jsfiddle.net/rniemeyer/ZnNCG/"></script>
<script src="https://raw.github.com/rniemeyer/rniemeyer.github.com/master/KnockMeOut/Scripts/knockout-1.3ctp.debug.js"></script>
<div id="list">
    <h2>Product</h2>
    <ul data-class="products">
        <li>
            <a href="#" data-class="nameText selectLink"></a>
            <span class="selected" data-class="nameText selectSpan"></span>
        </li>
    </ul>
</div>

<div id="editor" style="display: none" data-class="editor">
    <div>
        Name<br/>
        <input data-class="nameVal keydown" />
    </div>
    <div>
        Description<br/>
        <input data-class="descVal keydown" />
    </div>
    
    <div>
        <button data-class="dirty save">Save</button> 
        <button data-class="dirty cancel">Cancel</button> 
        <span data-class="saveStatus"></span>
    </div>
</div>

CSS

input { margin-bottom: 10px; }
h2 { font-size: 1.25em; font-weight: bold; }

.error { color: red }
.success { color: green }
.selected { color: blue; font-weight: bold; }

#list { 
    width: 100px; 
    height: 150px;
    border: solid 1px black; 
    padding: 5px; 
    float: left; 
}
#editor {
    height: 150px;
    border: solid 1px black;
    padding: 5px;
    margin-left: 120px;
}

JavaScript

//You can now create a bindingProvider that uses something different than data-bind attributes
ko.customBindingProvider = function(bindingObject) {
    this.bindingObject = bindingObject;
    
    //determine if an element has any bindings
    this.nodeHasBindings = function(node) {
        return node.getAttribute ? node.getAttribute("data-class") : false;
    };
    
    //return the bindings given a node and the bindingContext
    this.getBindings = function(node, bindingContext) {
        var i, j, bindingAccessor, binding, 
            result = {},
            classes = node.getAttribute("data-class");
        
        if (classes) {
            classes = classes.split(' ');  
            //evaluate each class, build a single object to return
            for (i = 0, j = classes.length; i < j; i++) {
               bindingAccessor = this.bindingObject[classes[i]];
               if (bindingAccessor) {
                   binding = typeof bindingAccessor == "function" ? bindingAccessor.call(bindingContext.$data) : bindingAccessor;
                   ko.utils.extend(result, binding);               
               }            
            }
        }
    
        return result;
    }; 
};


/*
This is the definition for an extended observable called a "protectedObservable"
-this would generally belong in a separate library
-the idea is that you can still use it just like any observable, but it has more features
-some of the extra features that it provides
  -when someone writes to it, the value is cached until you call its "commit" method
  -a "reset" method will update the bound fields back to the original value
  -the temporary value is exposed, in case you need to send it to the server
  -a dirtyFlag is exposed using a dependentObservable that checks the original vs. temp value
-now you can bind to your observable and any of its sub-observables (temp, isDirty, commit, reset)
-this post explains this idea in more detail:...