Update: I posted my code solution as an answer down below, which could help if someone wants to see a complete (and fairly simple) example of a KnockoutJs custom binding.
Problem:
When I use jQuery to set the checked status of a radio button… then it seems as if my KnockoutJs viewmodel does not track this change!
Scenario:
I have multiple large DIVs, and each DIV wraps one radio button. (This makes it easier for users to click the radio button by having a larger area to click on.) When the user clicks somewhere in the div, I want to check the radio button for them…. which works just fine. However, when attempting to read the value from the viewModel property bound to this radio button…. it has not been updated. 🙁
The only time the viewModel is updated is if I click DIRECTLY on the radio button inside the div. If I just click somewhere inside the div (which executes my jQuery), then…. even though the radio visibly becomes checked…. the knockoutjs viewModel property has not been updated with a new value.
Question:
Can someone please tell me how to change the checked status of a radio button using jQuery and have KnockoutJs play nicely and be updated as well?
Code is below, and here is jsFiddle: http://jsfiddle.net/CkEMa/67/
<script>
$(document).ready(function ()
{
var self = this;
function ViewModel()
{
this.HourlyOrSalary = ko.observable("");
}
viewModel = new ViewModel();
ko.applyBindings(viewModel, document.getElementById('divKnockout'));
// Click event for DIV around radio button
$('.divRadioWrapper').click(function ()
{
var radio = $(this).find('input[type="radio"]');
radio.prop('checked', true);
});
// Just for testing...
$('#testButton').click(function ()
{
var viewModelVal = viewModel.HourlyOrSalary();
alert('Value --> ' + viewModelVal);
});
});
</script>
<style>
.divRadioWrapper {
background-color: #dde9f5; /*#d8f5f0;*/ /* #dcfbff; */
width: 75px;
padding: 5px 10px;
border: 1px solid lightgray;
cursor: pointer;
margin-bottom: 10px;
}
</style>
<div id="divKnockout">
<div class="divRadioWrapper">
<input type="radio" name="formType" value="hourly" data-bind="checked: HourlyOrSalary"
/>Hourly</div>
<div class="divRadioWrapper">
<input type="radio" name="formType" value="salary" data-bind="checked: HourlyOrSalary"
/>Salary</div>
<br />
<input type="button" id="testButton" value="Display viewModel data" />
</div>
This is probably best handled by using the built-in KnockOut Checked Binding Handler. Alternatively you could build a much more flexible handler using a custom binding handler.
http://knockoutjs.com/documentation/custom-bindings.html
There is an excellent tutorial available too.
http://learn.knockoutjs.com/#/?tutorial=custombindings
The point of Knockout is to provide data-binding handlers and logic control so you don’t have to waste time doing it otherwise.
You can see the binding in action via a pre tag.
I just wanted to add to this here… the point of Knockout is to follow DRY principals (Don’t Repeat Yourself), and to practice good separation of the Model (pure JSON), ViewModel (observable Model + computed logic + validation), and View (HTML). This separation is key when it comes cutting out the stuff that you usually have to write for sending and receiving your data to the server, displaying that data to the user, and validating any user input before the whole operation starts over.
You could just hard/hand code the whole thing. It’s your own time (or foot) so patterns like these are intended to save you time (now and probably more late) when you are writing a large solution without having to duplicate functionality (read I didn’t say code here because functionality can be the same with different code paths).