Knockout - Extender

Simple checkbox and radio controls with extends to format input and output

by Ben Clayton

HTML

<script src="//knockoutjs.com/downloads/knockout-2.2.1.js"></script>
<script src="http://knockoutjs.com/downloads/knockout-3.2.0.js"></script>


Checkbox:
<input type="checkbox" data-bind="checked: mychk; css: mkchk.myinvalid() "></input>
<br/>
<br/> gender: Male:
<input type="radio" name="gender" data-bind="checked: mygender" value="M"></input>
Female:
<input type="radio" name="gender" data-bind="checked: mygender" value="F"></input>

CSS

body {
  padding: 10px;
}
input.myinvalid {
  background-color:red;
}

JavaScript

//=====================================================================================================================
// knockout extenders for observables
// register an extender class to handle adding classes to elements 'invalid' or 'dirty'
//   These observables have a css binding to the elements
// ex. of set 'myinvalid' class vm_1.travel.setInvalid(true)
ko.extenders.controlclass = function(target,linked_parent) {
	//add some sub-observables to our observable

	target.dirty = ko.observable(false);
	target.myinvalid = ko.observable(false);

	target.setInvalid=function(v){ // public method
	   if(typeof(v) != 'boolean') v=true; // default to true if parameter no given
	   this.myinvalid(v);
	}

	function setDirty(val){ // private method, parameter if used will be the value of form element
	   target.dirty(true);
	   target.myinvalid(false); // clear any invalid class which migh have been added by validation on form submit
	   if(linked_parent){ // clear parent observable(if there is one) which is normally a div wrapping all checkboxes in a set
			linked_parent( (linked_parent()?linked_parent():0) + (val?1:-1) );// keep qty selected on parent
			//console.log('linked_parent '+ linked_parent());
			linked_parent.myinvalid(false);

	   }
	}

	// set dirty class on any change on element value
	target.subscribe(setDirty);
	//return the original observable
	return target;
};


ko.extenders.checkbox = function(target, spare) {
  //create a writable computed observable to intercept writes to our observable
  // original observable value is a boolean for a checkbox
  //  so input and output need processing to work with 0/1 instead
  // also tolerate many other possible ways of saying true
  var result = ko.pureComputed({
    read: function() {
      return target() ? 1 : 0
    }, //return the original observables value but processed into how we want the value to be available to storage.
    write: function(newValue) {
      var current = target();
      newValue =...