Bootstrap 3 radio buttons with Knockout 3
Default radio buttons with Knockout bindings and checkedValue binding, removed data-toggle="buttons" and added in CSS to hide the circle part of the input type="radio"
by John Farrar
HTML
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.3.min.js"></script>
<script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-3.0.0.js"></script>
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
<div class="btn-group">
<label class="btn btn-primary" data-bind="css: { 'active': rSelect() === 'cyan' }">
<input type="radio" name="rSelect" id="rSelect1" data-bind="checked: rSelect, checkedValue: 'cyan'">Cyan
</label>
<label class="btn btn-primary" data-bind="css: { 'active': rSelect() === 'yellow' }">
<input type="radio" name="rSelect" id="rSelect2" data-bind="checked: rSelect, checkedValue: 'yellow'">Yellow
</label>
<label class="btn btn-primary" data-bind="css: { 'active': rSelect() === 'magenta' }">
<input type="radio" name="rSelect" id="rSelect3" data-bind="checked: rSelect, checkedValue: 'magenta'">Magenta
</label>
</div>
<p>
The Knockout view model value is: <span data-bind="text: rSelect"></span>
</p>
<div class="btn-group">
<label class="btn btn-primary" data-bind="css: { 'active': cbSelected('red') }">
<input type="checkbox" data-toggle="checkbox" name="cbSelect" data-value="red" data-bind="checkedButtons: cbSelect">Red
</label>
<label class="btn btn-primary" data-bind="css: { 'active': cbSelected('green') }">
<input type="checkbox" data-toggle="checkbox" name="cbSelect" data-value="green" data-bind="checkedButtons: cbSelect">Green
</label>
<label class="btn btn-primary" data-bind="css: { 'active': cbSelected('blue') }">
<input type="checkbox" data-toggle="checkbox" name="cbSelect" data-value="blue" data-bind="checkedButtons: cbSelect">Blue
</label>
</div>
<p>
The Knockout view model value is: <span data-bind="text: cbSelect"></span>
</p>
<div class="btn-group">
<label class="btn btn-primary"...
CSS
body {
padding: 15px;
}
label.btn > input[type='radio'],label.btn > input[type='checkbox']
{
display: none;
}
JavaScript
ko.bindingHandlers['checkedButtons'] = {
'init': function (element, valueAccessor, allBindingsAccessor) {
var type = element.getAttribute('data-toggle') || 'radio';
var updateHandler = function () {
var valueToWrite;
var isActive = !!~element.className.indexOf('active');
var dataValue = element.getAttribute('data-value');
if (type == "checkbox") {
valueToWrite = !isActive;
} else if (type == "radio" && !isActive) {
valueToWrite = dataValue;
} else {
return; // "checkedButtons" binding only responds to checkbox and radio data-toggle attribute
}
var modelValue = valueAccessor();
if ((type == "checkbox") && (ko.utils.unwrapObservable(modelValue) instanceof Array)) {
// For checkboxes bound to an array, we add/remove the checkbox value to that array
// This works for both observable and non-observable arrays
var existingEntryIndex = ko.utils.arrayIndexOf(ko.utils.unwrapObservable(modelValue), dataValue);
if (!isActive && (existingEntryIndex < 0))
modelValue.push(dataValue);
else if (isActive && (existingEntryIndex >= 0))
modelValue.splice(existingEntryIndex, 1);
} else {
if (modelValue() !== valueToWrite) {
modelValue(valueToWrite);
}
}
};
ko.utils.registerEventHandler(element, "click", updateHandler);
},
'update': function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
var type = element.getAttribute('data-toggle') || 'radio';
if (type == "checkbox") {
if (value instanceof Array) {
// When bound to an array, the checkbox being checked represents its value being present in that...