0

I am new to the use of the typeahead plugin and my javascript(not jquery) skills are terrible. This is my JSON:

{"Product":[
    {"@ProductCode":"C1010","@CategoryName":"Clips"},       
    {"@ProductCode":"C1012","@CategoryName":"Clips"},
    {"@ProductCode":"C1013","@CategoryName":"Clips"},
    {"@ProductCode":"C1014","@CategoryName":"Clips"},
    {"@ProductCode":"C1015","@CategoryName":"Clips", "EAN":"0987654321"}
]}

I have the typeahead bundle 0.10.5 And this is my js:

$(document).ready(function () {
    var products = new Bloodhound({
        datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        limit: 100,
        remote: {
            url: 'TypeAhead.ashx?q=%QUERY&cat=prod',
            filter: function (data) {
                return data.Products;
            }
        }
    });

    products.initialize();

    $("#tbSSearch").typeahead({
        highlight: true,
        minLength: 2
    }, {
        source: products.ttAdapter(),
        displayKey: function (products) {
            return products.product.code;
        },
        templates: {
            header:"<h3>Products</h3>"
        }
    });
});

Chrome console gives me:

Uncaught TypeError: Cannot read property 'length' of undefined

But that is in my jquery.2.1 (minified) lib and not the above js source. browser shows no popup below #tbSearch input.

as @Mike suggested, jsfiddle http://jsfiddle.net/gw0sfptd/1/ but I had to modify some stuff to work with local json. and this also does not work LOL

edit as David suggested, I should clean up my json. so it is now :

[{"Code":"C1010","Gtin13":0,"CategoryName":"Clips"},
 {"Code":"C1012","Gtin13":0,"CategoryName":"Clips"},
 {"Code":"C1013","Gtin13":0,"CategoryName":"Clips"}]

and the js:

remote: {
    url: 'TypeAhead.ashx?q=%QUERY&cat=prod',
    filter: function (products) {
        return $.map(products.results, function (product) {
            return {
                value: product.Code
            };
        });
    }
}
datumTokenizer: function (datum) {
    return Bloodhound.tokenizers.whitespace(datum.value);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,

But no working typeahead and no (useable)error in firefox console. My desired output would be a list of productcode, but also the category that they are in and the gtin13 (if not null) because the sql searches for all those three options. Should I make a javascript 'class' for product on the client side and parse the json to it? It is still unclear to me how the whole bloodhound thing works. (yes I have looked at the samples and read the docs of both typeahead and bloodhound) I don't know if it is possible but what my ultimate wish is, is that when you select an item from the typeahead suggestions this productdatasource links to productdetail.aspx and if you select an item of the categorydatasource (not visisble in this question) that it redirects the page to categorydetail.aspx

my chrome console error

JP Hellemons
  • 5,977
  • 11
  • 63
  • 128

2 Answers2

1

I've written a fiddle to demonstrate how to use your JSON (the local, not the remote, example) with typeahead.js:

http://jsfiddle.net/Fresh/f9rbeqyc/

In the following answer I chose to use the ProductCode as the suggestion, but you could obviously use the CategoryName if you wanted.

The key part of the code I used, along with some comments is here:

var json = '{"Product":[ ' +
 '{"@ProductCode\":\"C1010\",\"@CategoryName\":\"Clips\"},' +
 '{"@ProductCode\":\"C1012\",\"@CategoryName\":\"Clips\"},' +
 '{"@ProductCode\":\"C1015\",\"@CategoryName\":\"Clips\", \"EAN\":\"0987654321\"}]}';

// Parse the JSON string to a JSON object
var jsonObject = JSON.parse(json);

var products = new Bloodhound({
    // Use $.map() to create an array of ProductCode key value pairs
    local: $.map(jsonObject.Product, function (product) {
        return {
            value: product["@ProductCode"]
        };
    }),
    datumTokenizer: function (datum) {
        // Specify the variable within the datum to use as suggestion data
        // In this case it's the value
        return Bloodhound.tokenizers.whitespace(datum.value);
    },
    queryTokenizer: Bloodhound.tokenizers.whitespace
});

Also note that the datumTokenizer is specifying which value to use as a suggestion; an array of datums with a key called 'value' was created so we want to use 'value' as the display key. In your example you had "return products.product.code;" which isn't going to work as your datums don't have a field called "code", nor does your JSON(!).

You should be able to refer to my example and this answer to get your typeahead which uses remote working.

Community
  • 1
  • 1
Ben Smith
  • 19,589
  • 6
  • 65
  • 93
  • 1
    Unless there is a JavaScript error being thrown during JSON.parse, it is perfectly ok to have property names with any just about any character. You could do `data.Product[0]["@ProductCode"]` to access that value. – Greg Burghardt Nov 01 '14 at 01:45
  • 1
    I agree with Greg. "@ProductCode" isn't a valid variable name, but it's perfectly OK as a string in JSON and as an object key. – David Knipe Nov 01 '14 at 01:49
  • @GregBurghardt If you try to use "value: product.@ProductCode" in the code above, jslint will highlight the error. You'll also get an "Uncaught SyntaxError: Unexpected token ILLEGAL" in the browser console for that line. – Ben Smith Nov 01 '14 at 01:49
  • 1
    @BenSmith: you are correct. You cannot use the @ character if using dot notation to access the property value, however you can use Array notation just fine. – Greg Burghardt Nov 01 '14 at 01:51
  • @GregBurghardt Ah, I see what you mean now. Doh! I've updated my answer to reflect what you have said here in the comments. Thats something new I've learnt today anyway :) Hope whoever downvoted me sees my change :( – Ben Smith Nov 01 '14 at 01:58
0

Bergi was right. You've since fixed the capitalisation but not the pluralisation: it should be data.Product, not data.Products. Or perhaps the JSON you quoted was just data.Products, in which case the array is data.Products.Product.

BADGERFISH AND ARRAYS

One more thing you should be aware of. It looks like the JSON here is from badgerfish. I don't know whether you know what this means - basically it's a way of translating XML to JSON. The format will be different if there is only one product or none at all. For example, the following XML:

<Products>
    <Product ProductCode="C1010" ="Clips"/>
    <Product ProductCode="C1012" ="Clips"/>
</Products>

would give the following JSON:

{
    "Products": {
        "Product": [
            {"@ProductCode":"C1010","@CategoryName":"Clips"},       
            {"@ProductCode":"C1012","@CategoryName":"Clips"},
        ]
    }
}

but with just one product, badgerfish wouldn't know to make it an array, and you'd get this:

{
    "Products": {
        "Product": {"@ProductCode":"C1010","@CategoryName":"Clips"}
    }
}

and with no products, it wouldn't know to include "Product" at all:

{
    "Products": {}
}

The upshot is you should probably prepare for these cases. Similarly, it looks to me like the function displayKey will throw an exception unless it receives exactly one product.

I work on a project where our nodejs servers connect to backend servers. These backends traditionally used XML, but now optionally respond with JSON using badgerfish. This situation with arrays is so common that we have a utility function which takes data.Products.Product as an argument and converts it to an array, if it isn't already one.

David Knipe
  • 3,417
  • 1
  • 19
  • 19
  • Yes this is not the best json, but it was easy to convert xml to json. http://stackoverflow.com/questions/26605493/xdocument-or-xmldocument-to-json-with-c-sharp so I ended up with ugly json. sorry for that! perhaps I should parse my sql results to an `List` and convert that to json. – JP Hellemons Nov 03 '14 at 08:37