Search code examples
javascriptjsontypeahead.jsbloodhound

How do I use Twitters typeahead.js with Microsoft OData API's?


I have run into issues while writing a web-app to fetch JSON data from our CRM application.

What I want to do is use Twitter's typeahead.js plugin to remotely fetch customer information from our CRM application. Microsoft does provide a way to use JSON data to communicate. They call it OData. However, this does not look like your typical JSON responses. This is why I have trouble setting up the typeahead plugin with it.

When I send a GET request to the API URL, I get the following response:

{
    "d":{
        "results":[
            {
                "__metadata":{
                    "uri":"http://*****/*****/XRMServices/2011/OrganizationData.svc/AccountSet(guid'de227fde-fb40-dd11-b5d3-001cc46325e5')",
                    "type":"Microsoft.Crm.Sdk.Data.Services.Account"
                },
                "Name":"Some company as",
                "AccountId":"de227fde-fb40-dd11-b5d3-001cc46325e5"
            },
            {
                "__metadata":{
                    "uri":"http://*****/*****/XRMServices/2011/OrganizationData.svc/AccountSet(guid'5dc70a19-e91e-e311-9ad7-005056ac083a')",
                    "type":"Microsoft.Crm.Sdk.Data.Services.Account"
                },
                "Name":"Compnay AS",
                "AccountId":"5dc70a19-e91e-e311-9ad7-005056ac083a"
            }
        ]
    }
}

So here is the question: How do I setup Twitter's typeahead plugin to work with this data structure?

I want the Name value from the JSON response when displaying the suggestions. And I want to grab the associated AccountId value when a suggestion is selected.

This is what I got in my code so far:

HTML:

<!DOCTYPE html>
<html lang="no">
    <head>
        <title>Company :: Time Registrering</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" type="text/css" href="css/global.css" />
    </head>
    <body>

        <form action="" method="GET" autocomplete="off">
            <input type="text" name="account" id="account" placeholder="Kunde...">
        </form>

        <script type="text/javascript" src="js/jquery.min.js"></script>
        <script type="text/javascript" src="js/typeahead.js"></script>
        <script type="text/javascript" src="js/global.js"></script>
    </body>
</html>

JavaScript: (js/global.js)

$(document).ready(function () {
    var accounts = new Bloodhound({
        datumTokenizer: Bloodhound.tokenizers.obj.whitespace('Name'),
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        remote: "http://*****/*****/xrmservices/2011/OrganizationData.svc/AccountSet?$select=Name,AccountId&$filter=substringof('%QUERY',Name) and StateCode/Value eq 0"
    });

    accounts.initialize();

    $("#account").typeahead({
        hint: true,
        highlight: true,
        minLength: 2
    }, {
        name: 'account',
        displayKey: 'd.results[0].Name',
        source: accounts.ttAdapter()
    });

});

However: My code does not work. All I get is a text saying "undefined" under the input field. I have a suspicion that my datumTokenizer or the displayKey reference is incorrect. I don't fully understand the datumTokinizer. So if someone could enlighten me on it, I'd be thankful :)


Solution

  • You should make use of filter and use jQuery.map

    html

     <input type="text" name="account" id="account" placeholder="Kunde..." />
    

    js

    $(document).ready(function () {
    
    
    
        var accounts = new Bloodhound({
            datumTokenizer: Bloodhound.tokenizers.obj.whitespace('Name'),
            queryTokenizer: Bloodhound.tokenizers.whitespace,
            remote: {
            url : "https://gist.githubusercontent.com/anonymous/80a75d841a055ea0e480/raw/4eb8d4f1833d8a15cae1830097c090f5d581bd12/gistfile1.txt",
            filter: function(jsonValue) {
    
                    return $.map(jsonValue.d.results, function (result) {
                    return {
                        value: result.Name
                    };
                });
            }
        }     
    
    
        });
    
        accounts.initialize();
    
        $("#account").typeahead({
            hint: false,
            highlight: true,
            minLength: 2
        }, {
            source: accounts.ttAdapter()
        });
    
    });
    

    Fiddle here