Search code examples
knockout.jscustom-binding

How to update custom binding on button click?


With the click of a button, I want to update a d3js plot based on the values of two select elements. If I understand it right, simply calling the valueAccessor should fire off the update function, which is not happening in my code.

I have the following custom binding:

ko.bindingHandlers.plotSvg = {
    update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        valueAccessor();
        console.log("Update d3js plot");
    }
};

And the view:

    <div class="panel-body">
        <div class="row">
            <div class="col-xs-2">
                <select class="form-control"
                        data-bind="options: $root.options,
                                           optionsText: 'name',
                                           value: firstSelectedOption">
                </select>
            </div>
            <div class="col-xs-2">
                <select class="form-control"
                        data-bind="options: $root.options,
                                           optionsText: 'name',
                                           value: secondSelectedOption">
                </select>
            </div>
            <div class="col-xs-2 col-xs-offset-5">
                <button class="btn btn-primary" data-bind="click: updatePlot">
                    Visualise
                </button>
            </div>
        </div>
        <div data-bind="plotSvg: updatePlot"></div>
    </div>

updatePlot is a function in the viewmodel, and all it does is print a value at the moment.

edit The javascript: http://pastebin.com/2qKLf6yf The view: http://pastebin.com/TPvmKgxL


Solution

  • <div data-bind="plotSvg: updatePlot"></div> needs to reference a computed, otherwise knockout won't know when the value of something has changed and thus will never fire the custom binding's update method.

    I'd recommend something like:

    <div data-bind="plotSvg: selectedValue"></div>

    and

    ko.bindingHandlers.plotSvg = {
        update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
            viewModel.redrawPlot();
            console.log("Update d3js plot");
        }
    };
    

    where selectedValue is a computed that tracks currently selected value on your form, and gets updated when the button is clicked.

    Since you're using two values, you can do something like this in your viewModel to trigger an observable change:

    this.selectedValue = ko.observable(null);
    this.updatePlot = function(){
        this.selectedValue(this.firstSelectedOption()+""+this.secondSelectedOption()+"")
    }
    

    Doing it this way will ensure its a unique string each time one of the options change. If neither change, then the observable won't change, and the plot shouldn't redraw. Its brute force, but it should work with what you have setup.