Search code examples
javascriptknockout.jsviewmodelfuelux

How to Update Knockout ViewModel when using a Custom Binding


I'm trying to wire up a spinbox (an input with increment and decrement buttons) to KO, but I'm having some trouble with making sure my ViewModel updates correctly when a user interacts with the control.

The specific control I am using is Fuel UX's SpinBox: http://getfuelux.com/javascript.html#spinbox

The data-bind itself, is attached to the wrapping div, as that is the container for the entire control:

<div id="daysToComplete-spinner" data-initialize="spinbox" class="spinbox input-group" style="width: 150px;" data-bind="spinbox: daysToComplete, spinboxOptions: {min: 0, max: 180 }">
    <input class="form-control spinbox-input" placeholder="Days">
    <div class="spinbox-buttons input-group-btn">
        <button type="button" class="btn btn-cancel spinbox-up">
            <span class="fa fa-angle-up"></span><span class="sr-only">Increase</span>
        </button>
        <button type="button" class="btn btn-cancel spinbox-down">
            <span class="fa fa-angle-down"></span><span class="sr-only">Decrease</span>
        </button>
    </div>
</div>

I wrote a binding handler, which seems to run and build the control, but there are a couple things I am having difficultly implementing:

  • I want to register an event handler. When a user clicks the up or down button (the spans), I need to make sure the ViewModel's Property is updated with the new value. I've tried using valueAccessor(), but it gives me the value, not the observable property which I was expecting (after reading the docs on the ko site).
  • I want to make sure that if a user types a number into the input box, the ViewModel's Property is updated. The data-binding is on the div, so I'm assuming knockout wont "know" that the input inside the div has changed...how do I accomplish this? Same event handler?

Here is what I've got so far on my handler:

$(function () {
    ko.bindingHandlers.spinbox = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {

            var options = allBindingsAccessor().spinboxOptions || {};

            $(element).spinbox(options);

            var value = ko.utils.unwrapObservable(valueAccessor());

            if (value) {
                $(element).spinbox("value", value);
            }

            ko.utils.registerEventHandler(element, "changed.fu.spinbox", function (event, value) {
                var modelValue = valueAccessor(),
                modelValue(value); //it crashes here "not a function"

            });
        },
        update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
            var value = ko.utils.unwrapObservable(valueAccessor());
            $(element).spinbox("value", value);

            console.log(value);
        }
    };
});

Any guidance as to what I am missing would be appreciated.


Solution

  • I don't know what your viewmodel looks like but can you make sure that you are not overwriting the observable with a value during previous operations.

    If at some point you did something like that:

    viewmodel.daysToComplete = 4

    Then daysToComplete property is not an observable anymore. As a result valueAccessor() will not return an observable but 4 which is what you seem to describe.