KnockoutJS bitField binding
by sukobuto
HTML
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
<script src="http://sukobuto.com/files/js/knockout-es5.min.js"></script>
<h1>KnockoutJS bitField binding</h1>
<h2>Weekdays</h2>
<ul data-bind="foreach: ko.utils.range(0,6)">
<li>
<label>
<input type="checkbox"
data-bind="bitField: $root.week_days, checkedFlag: $root.weekday_flags[$data]"/>
<span data-bind="text: $root.weekday_labels[$data]"> </span>
</label>
</li>
</ul>
Value:
<input type="number" data-bind="value: week_days"/>
0b<span data-bind="text: week_days_bin" class="digits"> </span>
<h3>Toggle button style</h3>
<div class="btn-group" data-bind="foreach: ko.utils.range(0,6)">
<label class="btn btn-primary" data-bind="css: { active: $parent.week_days() & $parent.weekday_flags[$data] }">
<input type="checkbox" style="display: none;"
data-bind="bitField: $root.week_days, checkedFlag: $root.weekday_flags[$data]"/>
<span data-bind="text: $root.weekday_labels_2[$data]"> </span>
</label>
</div>
<h2>Linux File Mode</h2>
<ul data-bind="foreach: file_modes">
<li>
<label>
<input type="checkbox" data-bind="bitField: $root.file_mode, checkedFlag: flag"/>
<span data-bind="text: label"> </span>
</label>
</li>
</ul>
Value:
<input type="number" data-bind="value: file_mode"/>
0o<span data-bind="text: file_mode_oct" class="digits"> </span>
CSS
.digits {
color: blue;
}
JavaScript
// bitField binding
ko.bindingHandlers['bitField'] = {
'after': ['value', 'attr'],
'init': function (element, valueAccessor, allBindings) {
var propWriters = allBindings()['_ko_property_writers'];
function checkedFlag() {
return allBindings['has']('checkedFlag')
? ko.unwrap(allBindings.get('checkedFlag'))
: element.value;
}
function updateModel() {
// This updates the model value from the view value
// It runs in response to DOM events (click) and changes in checkedFlag.
var isChecked = element.checked,
elemValue = checkedFlag();
// When we're first setting up this computed, don't change any model state.
if (!shouldSet) {
return;
}
var modelValue = valueAccessor();
var unwrapped = ko.unwrap(modelValue);
if (oldElemValue !== elemValue) {
if (isChecked) {
if (oldElemValue !== undefined) {
unwrapped &= ~oldElemValue;
}
unwrapped |= elemValue;
}
oldElemValue = elemValue;
} else {
if (isChecked) {
unwrapped |= elemValue;
} else {
unwrapped &= ~elemValue;
}
}
if (ko.isObservable(modelValue)) {
modelValue(unwrapped);
} else if (propWriters['bitField']) {
propWriters.bitField(unwrapped);
} else {
valueAccessor(unwrapped);
}
}
function updateView() {
// This updates the view value from the model value.
// It runs in response to changes in the bound (checked) value.
var modelValue = ko.unwrap(valueAccessor());
...