Search code examples
javascriptknockout.jsko.observablearray

ClickToEdit button with KnockoutJS


I'm creating a custom binding that will display an anchor that when clicked turns into a textbox. Once the user enters a value and hits the enter key the value would be pushed into the observableArray that is passed to the binding.

The majority of the binding was taken from Ryan Niemeyers example - found here

html

<div data-bind="clickToEdit: { data: items, buttonText: 'New Item' }"></div>

binding

ko.bindingHandlers.clickToEdit = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, context) {
        var options = valueAccessor() || {};

        var editing = ko.observable(false);
        var newValue = ko.observable();

        var button = $('<a class="btn btn-mini" href="#">' + options.buttonText + '</a>').prepend('<span class="icon-plus"></span>').appendTo(element)[0];
        var input = $('<input type="text">').appendTo(element)[0];

        ko.applyBindingsToNode(button, {
            hidden: editing,
            click: function () {
                editing(true);
            }
        });

        ko.applyBindingsToNode(input, {
            value: newValue,
            visible: editing,
            hasFocus: editing,
            //valueUpdate: 'afterkeydown',
            onEnter: function(data) {
                debugger;
                options.data.push(data);
                editing(false);
            }
        });
    }
};

The problem is that in the function that is bound to the onEnter binding the data parameter is always undefined so the new value is never pushed into the array.

Please see my fiddle for a complete example


Solution

  • Two things that can help:

    • The applyBindingsToNode call accepts a third argument where you can provide the context. In some of my samples, the context was irrelevant, because it was writing directly to an observable and did not need the data. So, you can pass context (or viewModel) in as the third argument to your second applyBindingsToNode call.

    • If I understand what you are trying to do though, then I would think that you could just do something like: options.data.push(new Item(newValue()); and not need to worry about the context/data (which would be the overall view model, as you are in the top-level scope).

    Example: http://jsfiddle.net/rniemeyer/Pmns4/