Wednesday, May 24, 2017

Knockout twitter typeahead with remote json

Twitter typeahead is a very powerful and easy to use library which can display typeahead on any text box. However if we want to use it with Knockout then ideally we could add a custom binding which handles creation and data queries.

In a project the requirement was to display typeahead using knockout binding which can fetch suggestions from remote web service. The returned data would be in JSON format and it contains text and value fields. I need to display suggestions in the format 'text (value)' but when user selects a suggestion, the corresponding knockout observable should be update with the suggestions value only. e.g. If user selects a suggestion displayed as 'London (LN)' then the observable should store 'LN' only.

After searching online for a while I could find the post below which shows how the latest typeahead can be used to fetch data from a remote web service.

https://stackoverflow.com/questions/35451319/typeahead-change-source

And this google search returns several results for custom knockout typeahead bindings. Combining it all, I finally settled on a custom knockout binding like so:

// Bind twitter typeahead
    koObject.bindingHandlers.typeahead = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
            var $element = $(element);
            var allBindings = allBindingsAccessor();
            var url = ko.unwrap(valueAccessor());
            var criteria = allBindings.criteria;

            var typeaheadOpts = {
                name: 'employees',
                source: function(query, sync, async) {
                   
                    $.ajaxSetup({ timeout: 300000 });
                    criteria.SearchText = query;
                    var data = JSON.stringify(criteria);
                    // call ajax to service
                    $.ajax({
                        type: "POST", //GET or POST or PUT or DELETE verb
                        url: url, // locates of the wcf service method
                        contentType: 'application/json',
                        crossDomain: true,
                        data: data,
                        cache: false,
                        success: function (data, statusText, xhr) {
                            data = data.map(function (item) {
                                item.textValue = item.text + " (" + item.value + ")";
                                return item;
                            })
                            async(data);
                        },
                        error: function (xhr, status) {
                            console.log(xhr.error);
                            console.log(status);
                        }
                    });
                },
                displayKey: 'textValue'
            };

            if (allBindings.typeaheadOptions) {
                $.each(allBindings.typeaheadOptions, function (optionName, optionValue) {
                    typeaheadOpts[optionName] = koObject.utils.unwrapObservable(optionValue);
                });
            }

            $element.attr("autocomplete", "off").typeahead({
                hint: true,
                highlight: true,
                minLength: 1
            }, typeaheadOpts)
            .on('typeahead:selected', function (el, datum) {
                console.dir(datum);
                allBindings.valueField(datum.value);
            });
        }
    };


This binding can then be used like:

<input type="text" data-bind="valueField: SelectedEmployee, typeahead: TypeAheadUrl, criteria: Criteria" />

And finally the view model should have properties SelectedEmployee, TypeAheadUrl and Criteria like so:

                var ViewModel = function () {
            this.Criteria = {Departments: ['FIN', 'HR', 'COM']};
            this.TypeAheadUrl = 'https://local.getdata.com';
            this.SelectedEmployee = ko.observable('');
        };

c# httpclient The remote certificate is invalid according to the validation procedure: RemoteCertificateNameMismatch

 If we get this error while trying to get http reponse using HttpClient object, it could mean that certificate validation fails for the remo...