Thursday, March 29, 2018

Knockout datalist binding when input selected

I recently had the need to know when an item from a datalist was selected vs a user entering text into the input so that I could trigger a different behavior in the UI. This is what I came up with based on the 'input' event that is raised. When raised due to the user typing, the event's originalEvent property is an InputEvent and has an inputType property. If the user selects a value, the event's originalEvent property is an Event and does not have that property. Not sure if there's a better way, but this works! :)

    /**
     * @desc This binding will trigger when a user selects an item from
     * the data list and will pass the selected value to the specified function
     *
     */
    ko.bindingHandlers.datalistInput = {
        init: function (element, valueAccessor) {
            $(element).on('input', function (e) {
                if (e && e.originalEvent && e.originalEvent.type === "input" && !e.originalEvent.inputType) {
                    var functionToExecute = ko.utils.unwrapObservable(valueAccessor());
                    if (functionToExecute && typeof (functionToExecute) === 'function') {
                        functionToExecute(e.target.value);
                    }
                }
            });
        }
    }

This can be used with an input and datalist like this:

    <input class="form-control"
           data-bind="textInput: userInput,
                      datalistInput: function(selectedText) { console.log(selectedText); }"
           list="mydatalist">
    <datalist id="mydatalist">
        <!-- ko foreach: listOptions -->
        <option data-bind="text: $data"></option>
        <!-- /ko -->
    </datalist>
In this case, textInput will bind to anything the user enters as well as any value selected from the datalist by the user. The datalistInput will only be triggered when the user selects a value from the datalist.