Click here to Skip to main content
12,500,878 members (44,664 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

6.3K views
4 bookmarked
Posted

Replacing Radio Buttons with a Button Group using Bootstrap and #KnockoutJS

, 27 Apr 2015 CPOL
Rate this:
Please Sign up or sign in to vote.
Replacing Radio Buttons with a Button Group using Bootstrap and #KnockoutJS

Problem

Radio buttons are hard-to-see, not easy to select, and let's face it, quite mundane. You would like to replace these radio buttons with a group of buttons that represent the same functionality, e.g., only one of the options may be selected at any given time.

Solution

Leveraging Bootstrap which provides many incredibly styled components for buttons, alert boxes, tables, forms, etc., regular radio buttons will be replaced by a button group (see screenshot below). Knockout.js will be used to create a custom data binding that will make the group of buttons act like regular radio buttons (with a nicer look of course).

radiobuttongroup

This example assumes you have a basic understanding of both Bootstrap and Knockout.js.

The versions used for this example are 3.3.4 for Bootstrap and 3.3 for Knockout.js. This example should be compatible with older versions of these frameworks.

Discussion

Before getting started, the frameworks required must be setup. Bootstrap can be installed either via their CDN or downloaded. This example will use the CDN; however, I suggest you use the option that bests suits your needs. Knockout.js has been downloaded and saved into the same location as where the example lives. Ensure you update this location based on your project's location. And finally, a little bit of jQuery is used within the Knockout Custom Binding. Once again, this example is leveraging the CDN; however, you may download and include it if you like.

For this example to work, it requires two things. A group of buttons that will contain what would be the options if they were radio buttons and a Knockout observable value that will be used in the data-binding. This observable will contain the selected option. Below contains the HTML markup and the extremely basic Knockout ViewModel.

<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
</head>
<body>
<div class="btn-group">
<button type="button" class="btn btn-default" 
data-bind="radioButtonGroup: selectedOption, radioValue: 'option1'">Option 1</button>
<button type="button" class="btn btn-default" 
data-bind="radioButtonGroup: selectedOption, radioValue: 'option2'">Option 2</button>
<button type="button" class="btn btn-default" 
data-bind="radioButtonGroup: selectedOption, radioValue: 'option3'">Option 3</button>
</div>

<script src="knockout.js"></script>
<script src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
<script>
function ViewModel() {
var self = this;

self.selectedOption = ko.observable();
};

var viewModel = new ViewModel();
ko.applyBindings(viewModel);
</script>
</body>
</html>

This example won't work just yet. If you look closely, each of the HTML buttons contain a data-binding called radioButtonGroup (this will be defined momentarily). This is the custom data binding that will accomplish our fancy button group. Supplied to the new data binding is the observable variable, aptly named, selectedOption. A second value is supplied as well named radioValue. This should be set to the value you wish the selectedOption variable to contain when the user presses that button.

To complete this example, the custom binding needs to be created. Custom bindings are added under the ko.bindingHandlers namespace. A binding handler is defined by creating an init and update function. Either are optional, but at least one must be defined. The init function is called when Knockout is first data bound to the page. This is where you would create event listeners, etc. The update function is called every time the value changes (including on first load). This function would be used if you wished to perform specific actions each time the value changed.

The radioButtonGroup custom binding only requires the init function because event listeners for the click event will be used to track all changes. The following JavaScript code should be placed (or included from a separate file) after the Knockout framework is included, but before your ViewModel is defined and the Knockout bindings are applied.

<script>
ko.bindingHandlers.radioButtonGroup = {
init: function (element, valueAccessor, allBindings, viewModel, context) {
var $buttons, $element, observable;
observable = valueAccessor();
if (!ko.isWriteableObservable(observable)) {
throw "You must pass an observable or writeable computed";
}
$element = $(element);
if ($element.hasClass("btn")) {
$buttons = $element;
} else {
$buttons = $(".btn", $element);
}
elementBindings = allBindings();
$buttons.each(function () {
var $btn, btn, radioValue;
btn = this;
$btn = $(btn);
radioValue = elementBindings.radioValue || 
$btn.attr("data-value") || $btn.attr("value") || $btn.text();
$btn.on("click", function () {
observable(ko.utils.unwrapObservable(radioValue));
});
return ko.computed({
disposeWhenNodeIsRemoved: btn,
read: function () {
$btn.toggleClass("active", observable() === ko.utils.unwrapObservable(radioValue));
$btn.toggleClass("btn-info", observable() === ko.utils.unwrapObservable(radioValue));
}
});
});
}
};
</script>

The init function accepts 5 parameters. The element being data bound, the variable it is data bound too, all other bindings on this element, the entire ViewModel (however this is being deprecated), and the bindingContext, this is the new way proposed to access the ViewModel by using bindingContext.$data.

Inside the init function, it first verifies it is dealing with an observable property. Using the HTML element with the data binding (and some jQuery), the related buttons are found and stored in a variable. These buttons are then looped through and the click event is listened for. When it occurs, the observable property associated to the data binding's value is updated with the option selected. And finally a computed variable is defined, that if the observable's value is equal the value of the button, the class active and btn-info are added to identify which button is currently selected.

That completes this example, as always you can find the full source code on GitHub.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Jamie Munro
Software Developer (Senior)
Canada Canada
I am the author of five books: ASP.NET MVC 5 with Bootstrap and Knockout.js, Knockout.js: Building Dynamic Client-Side Web Applications, 20 Recipes for Programming PhoneGap, 20 Recipes for Programming MVC 3, and Rapid Application Development with CakePHP.

I enjoy reading and writing and I like to blog as much as I can on my personal blog EndYourIf.

I'm a father of three kids, twins (boy and a girl) and another baby girl.

You may also be interested in...

Pro
Pro

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160919.1 | Last Updated 28 Apr 2015
Article Copyright 2015 by Jamie Munro
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid