Bootstrap type ahead bind values with knockoutjs n

2019-02-20 00:20发布

问题:

I have a ViewModel that I created with knockout which contains all the info for my product. And It looks like this:

var ProductViewModelDS = function (data) {
        var self = this;
        self.ProductSKUID = ko.observable(data.ProductSKUID);
        self.ProductID = ko.observable(data.ProductID);
        self.ProductSKUStockCode = ko.observable(data.ProductSKUStockCode);
        self.ProductSKUManufacturePartNumber = ko.observable(data.ProductSKUManufacturePartNumber);
        self.ProductSKUName = ko.observable(data.ProductSKUName);
        self.ProductSKUPrice = ko.observable(data.ProductSKUPrice);
        self.ProductSKUSpecialPrice = ko.observable(data.ProductSKUSpecialPrice);
        self.ProductSKUIsOnSpecial = ko.observable(data.ProductSKUIsOnSpecial);
        self.ProductSKUMinimumOrderQty = ko.observable(data.ProductSKUMinimumOrderQty);
        self.ProductSKUMaximumOrderQty = ko.observable(data.ProductSKUMaximumOrderQty);
        self.ProductSKUCurrentStock = ko.observable(data.ProductSKUCurrentStock);
    }

I want to use the bootstrap type ahead, so that i can view the product info. I found this Article which gives me the event handler to do this.

But as soon as I start typing I get this error in my console:

Uncaught TypeError: Object [object Object] has no method 'toLowerCase' bootstrap.js:1831 Uncaught TypeError: Cannot use 'in' operator to search for 'length' in 1

This is my JSON I get from my AJAX call:

{
"d": [
    {
        "__type": "POS.Tracntrace.Member_Only.DAL.Models.CreateOrder.CreateOrderProductModel",
        "ProductSKUID": 15,
        "ProductID": 1,
        "ProductSKUStockCode": "BPUNIRM1131",
        "ProductSKUManufacturePartNumber": "600284403 1213",
        "ProductSKUName": "DSD 1131 Remote Control",
        "ProductSKUPrice": 84,
        "ProductSKUSpecialPrice": null,
        "ProductSKUIsOnSpecial": "false",
        "ProductSKUMinimumOrderQty": 1,
        "ProductSKUMaximumOrderQty": 10000,
        "ProductSKUCurrentStock": 100
    },
    {
        "__type": "POS.Tracntrace.Member_Only.DAL.Models.CreateOrder.CreateOrderProductModel",
        "ProductSKUID": 16,
        "ProductID": 2,
        "ProductSKUStockCode": "SDF213",
        "ProductSKUManufacturePartNumber": "55511545121",
        "ProductSKUName": "DSD 1132",
        "ProductSKUPrice": 599,
        "ProductSKUSpecialPrice": null,
        "ProductSKUIsOnSpecial": "false",
        "ProductSKUMinimumOrderQty": 1,
        "ProductSKUMaximumOrderQty": 10000,
        "ProductSKUCurrentStock": 100
    },
    {
        "__type": "POS.Tracntrace.Member_Only.DAL.Models.CreateOrder.CreateOrderProductModel",
        "ProductSKUID": 17,
        "ProductID": 3,
        "ProductSKUStockCode": "RPAIRDRHD",
        "ProductSKUManufacturePartNumber": "600284400 1018",
        "ProductSKUName": "HD PVR Remote Control",
        "ProductSKUPrice": 250,
        "ProductSKUSpecialPrice": null,
        "ProductSKUIsOnSpecial": "false",
        "ProductSKUMinimumOrderQty": 1,
        "ProductSKUMaximumOrderQty": 10000,
        "ProductSKUCurrentStock": 100
    },
    {
        "__type": "POS.Tracntrace.Member_Only.DAL.Models.CreateOrder.CreateOrderProductModel",
        "ProductSKUID": 18,
        "ProductID": 4,
        "ProductSKUStockCode": "1131",
        "ProductSKUManufacturePartNumber": "DSD1131",
        "ProductSKUName": "DSD1131 DVB-S",
        "ProductSKUPrice": 499,
        "ProductSKUSpecialPrice": 498,
        "ProductSKUIsOnSpecial": "false",
        "ProductSKUMinimumOrderQty": 1,
        "ProductSKUMaximumOrderQty": 10000,
        "ProductSKUCurrentStock": 100
    },
    {
        "__type": "POS.Tracntrace.Member_Only.DAL.Models.CreateOrder.CreateOrderProductModel",
        "ProductSKUID": 19,
        "ProductID": 5,
        "ProductSKUStockCode": "4660",
        "ProductSKUManufacturePartNumber": "DSR4660",
        "ProductSKUName": "DSR4660 HD DVB-S2",
        "ProductSKUPrice": 1499,
        "ProductSKUSpecialPrice": null,
        "ProductSKUIsOnSpecial": "false",
        "ProductSKUMinimumOrderQty": 1,
        "ProductSKUMaximumOrderQty": 100,
        "ProductSKUCurrentStock": 100
    },
    {
        "__type": "POS.Tracntrace.Member_Only.DAL.Models.CreateOrder.CreateOrderProductModel",
        "ProductSKUID": 20,
        "ProductID": 6,
        "ProductSKUStockCode": "DVR3000",
        "ProductSKUManufacturePartNumber": "MCSDPVR3000",
        "ProductSKUName": "DVR3000",
        "ProductSKUPrice": 1500,
        "ProductSKUSpecialPrice": null,
        "ProductSKUIsOnSpecial": "false",
        "ProductSKUMinimumOrderQty": 1,
        "ProductSKUMaximumOrderQty": 10000,
        "ProductSKUCurrentStock": 100
    },
    {
        "__type": "POS.Tracntrace.Member_Only.DAL.Models.CreateOrder.CreateOrderProductModel",
        "ProductSKUID": 21,
        "ProductID": 7,
        "ProductSKUStockCode": "DE45",
        "ProductSKUManufacturePartNumber": "N/A",
        "ProductSKUName": "ELSAT 45CM MILD STEEL DISH",
        "ProductSKUPrice": 560,
        "ProductSKUSpecialPrice": null,
        "ProductSKUIsOnSpecial": "false",
        "ProductSKUMinimumOrderQty": 1,
        "ProductSKUMaximumOrderQty": 10000,
        "ProductSKUCurrentStock": 100
    },
    {
        "__type": "POS.Tracntrace.Member_Only.DAL.Models.CreateOrder.CreateOrderProductModel",
        "ProductSKUID": 22,
        "ProductID": 8,
        "ProductSKUStockCode": "DE60",
        "ProductSKUManufacturePartNumber": "N/A",
        "ProductSKUName": "ELSAT 60CM MILD STEEL DISH",
        "ProductSKUPrice": 900,
        "ProductSKUSpecialPrice": null,
        "ProductSKUIsOnSpecial": "false",
        "ProductSKUMinimumOrderQty": 1,
        "ProductSKUMaximumOrderQty": 10000,
        "ProductSKUCurrentStock": 100
    }
]
}

I just want to be able to search for ProductSKUName, ProductSKUStockCode or ProductSKUManufactureStockCode.

Any Advice will be greatly appreciated.

HTML:

    <div class="well">
    <input type="text" data-bind="typeahead: { target: ProductViewModel, source: Products }" />
</div>

Edit:

My Javascript:

        ko.bindingHandlers.typeahead = {
        init: function (element, valueAccessor) {
            var binding = this;
            var elem = $(element);
            var value = valueAccessor();
            elem.typeahead(
              {
                  source: function () { return ko.utils.unwrapObservable(value.source); },
                  onselect: function (val) { value.target(val); }
              });
            elem.blur(function () { value.target(elem.val()); });
        },
        update: function (element, valueAccessor) {
            var elem = $(element);
            var value = valueAccessor();
            elem.val(value.target());
        }
    };


    var ProductViewModelDS = function (data) {
        var self = this;
        self.ProductSKUID = ko.observable(data.ProductSKUID);
        self.ProductID = ko.observable(data.ProductID);
        self.ProductSKUStockCode = ko.observable(data.ProductSKUStockCode);
        self.ProductSKUManufacturePartNumber = ko.observable(data.ProductSKUManufacturePartNumber);
        self.ProductSKUName = ko.observable(data.ProductSKUName);
        self.ProductSKUPrice = ko.observable(data.ProductSKUPrice);
        self.ProductSKUSpecialPrice = ko.observable(data.ProductSKUSpecialPrice);
        self.ProductSKUIsOnSpecial = ko.observable(data.ProductSKUIsOnSpecial);
        self.ProductSKUMinimumOrderQty = ko.observable(data.ProductSKUMinimumOrderQty);
        self.ProductSKUMaximumOrderQty = ko.observable(data.ProductSKUMaximumOrderQty);
        self.ProductSKUCurrentStock = ko.observable(data.ProductSKUCurrentStock);
        self.SearchText = ko.computed(function () {
            return self.ProductSKUName() + ' ' + self.ProductSKUStockCode() + ' ' + self.ProductSKUManufacturePartNumber();
        });
    }

    var ProductViewModel = function (Products) {
        var self = this;

        self.Products = ko.observableArray(Products);

        $.ajax({
            url: "CreateOrder.aspx/GetAvailibleProducts",
            data: '{}',
            type: "POST",
            contentType: "application/json; charset=utf-8",
            dataType: "JSON",
            timeout: 10000,
            success: function (Result) {
                var MappedProducts =
              $.map(Result.d,
             function (item) {
                 return new ProductViewModelDS(item);
             }
               );
                self.Products(MappedProducts);
            },
            error: function (xhr, status) {
                alert(status + " - " + xhr.responseText);
            }
        });

        self.Save = function () {
            alert('Could Now Save: ' + ko.mapping.toJSON(self.Products));
        }
    };

    $(document).ready(function () {
        var VM = new ProductViewModel();
        ko.applyBindings(VM);
    })

回答1:

Looking at the link you provided, have you not got your target and source the wrong way round?

Basically, it appears that the type ahead plugin wants an array of string values it can use for the auto-complete suggestions. Add a SearchText property to the ProductViewModel that loops through all the products and puts the three fields you want into the searchterm array.

var ProductViewModel = function () {
    self.SearchText = ko.computed(function()
    {
        var searchableTerms = [];
        ko.utils.arrayForEach(self.Products(), function (item)
        {
            searchableTerms.push(item.ProductSKUName());
            searchableTerms.push(item.ProductSKUStockCode());
            searchableTerms.push(item.ProductSKUManufacturePartNumber());
        });
        return searchableTerms;
    });
};

Then update the html to something like:

<div class="well">
    <input type="text" data-bind="typeahead: { target: Products, source: SearchText }" />
</div>