Wijmo Combobox with an AJAX Data Source (Wikipedia API)

  • report
    Disclaimer
    Click for Disclaimer
    This Project is over a year old (first published about 3 years ago). As such, please keep in mind that some of the information may no longer be accurate, best practice, or a reflection of how I would approach the same thing today.
  • infoFull Project Details
    info_outlineClick for Full Project Details
    Date Posted:
    Feb. 27, 2016
Want to skip right to the working demo? Click here.

Intro:

For a recent project, I needed to implement a ComoBox that a user could type into, and they would be offered a dropdown list of matching selections based on a third-party API (think of how Google Auto-suggest acts). This is difficult for a number of reasons, which I list further down. However, an easy workaround/hack I found was to use JSONP requests (courtesy of this answer on Stack Overflow), combined with the Wijmo 3 UI Widget Framework. The Wijmo ComboBox widget can be a little confusing to implement though, especially with a dynamic data source, which is the main reason why I came up with a demo and wrote this post (as a weekend project).

Building a Wijmo ComboBox with an AJAX Data Source

Working Example:

Since this is probably what most people are interested in seeing first, here is a working example. I am using the (unofficial) Wikipedia API as my source, so as you type in the ComboBox, you will see entries from Wikipedia matching your query. Pretty cool, right?

Step 1 – Load Wijmo resources, widgets, and set initial options

The first step is to load all the necessary Wijmo resources (JS, CSS, etc). It is best to do this in the head of your document. Here is how I did it:

<head>
<!--         Wijmo Dependencies       -->
<!--Theme-->
<link href="http://cdn.wijmo.com/themes/aristo/jquery-wijmo.css" rel="stylesheet" type="text/css" />

<!--Wijmo Widgets CSS-->
<link href="http://cdn.wijmo.com/jquery.wijmo-pro.all.3.20153.83.min.css" rel="stylesheet" type="text/css" />

<!--RequireJs-->
<script type="text/javascript" src="http://cdn.wijmo.com/external/require.js"></script>

<!--Config Jquery for Wijmo-->
<script type="text/javascript">
    requirejs.config({
        baseUrl: "http://cdn.wijmo.com/amd-js/3.20153.83",
            paths: {
                "jquery": "jquery-1.11.1.min",
                "jquery-ui": "jquery-ui-1.11.0.custom.min",
                "jquery.ui": "jquery-ui",
                "jquery.mousewheel": "jquery.mousewheel.min",
                "globalize": "globalize.min"
            }
    });
</script>
<!--         End Wijmo Dependencies       -->

<!--		 Setup Wijmo ComboBox		  -->
<script>
require(["wijmo.wijcombobox"], function () {
        $(document).ready(function () {
            $("#Wikipedia_ComboBox").wijcombobox({
                data: [{
                    label: 'Please start typing to get suggestions.',
                    value: 'null'
                }]
            });
			$("#Wikipedia_ComboBox").wijcombobox({
				select : function(){afterSelection()}
			});
			$("#Wikipedia_ComboBox").wijcombobox("option","autoComplete",false); //turn off auto-complete
			$("#Wikipedia_ComboBox").wijcombobox("option","forceSelectionText",false);
        });
    });
</script>
<!--			End WijmoComboBox Setup				-->
</head>

You don’t really need to worry about the above HTML, just swap “Wikipedia_ComboBox” for the ID of your own ComboBox. The only thing to note is that the

$("#Wikipedia_ComboBox").wijcombobox({select : function(){afterSelection()}});

code is a cool trick – Wijmo is letting me define a custom function I want to have called after a user makes a selection within the ComboBox, which in this case, is my afterSelection function (see more later).

Step 2: Write out your HTML

Obviously, this step is going to be very different for each person and their needs. In my case, I’m just creating a demo, so my HTML is very simple – a single input that will get turned into a combobox, and an extra div where the selected Wikipedia link will be displayed.

<body>
<input id="Wikipedia_ComboBox" placeholder="Type to Search and Use Dropdown" class="wijmo-wijcombobox wijcombobox" style="width:500px; height:50px;"/>

<div id="Entry_Selection">
<div>Here is the link of your selected Wikipedia entry: </div>
<div id="Wikipedia_Link_Area"><a href="null" target="_blank" id="Wikipedia_Link">Entry Link</a></div>
</div>
</body>

Step 3: Write out the Javascript!

Here is the basic flow of my Javascript:

  1. User enters a character into the combobox, which triggers a new query
  2. I use the contents of the ComboBox as the input for my Wikipedia search (the search parameter). I use the JSONP-hack function to make the request with my query and send the result to my callback function
  3. After a response is received from the Wikipedia API, my custom callback function is triggered. It parses the return data (which is JSON formatted) into an array, which is then passed to the Wijmo combobox via the data option. The standard format for the Wijmo data option is [{label: label, value: value}].
  4. The user selects a Wikipedia entry from the dropdown auto-suggest list
  5. Wijmo remembers I gave it a function to call if a selection is made, so it triggers that function. My function kicks in and updates the Wikipedia Link section with a link to the selected Wikipedia entry.

And, here is all the Javascript:

var dynamicData = [];

function jsonp(url, callback) {
    var callbackName = 'jsonp_callback_' + Math.round(100000 * Math.random());
    window[callbackName] = function(data) {
        delete window[callbackName];
        document.body.removeChild(script);
        callback(data);
    };

    var script = document.createElement('script');
    script.src = url + (url.indexOf('?') >= 0 ? '&' : '?') + 'callback=' + callbackName;
    document.body.appendChild(script);
}

function handleAjaxResponse(input){
	var temp = document.getElementById("Wikipedia_ComboBox").value;
	dynamicData = []; // reset our data source before filling it with resopnse
	for (x = 0; x < input[1].length; x++){
		dynamicData.push({"label": input[1][x].toString(), "value": input[3][x].toString()});
	}
	$("#Wikipedia_ComboBox").wijcombobox({
		data: dynamicData
	});
	document.getElementById("Wikipedia_ComboBox").value = temp; // Necessary for a stupid reason; manipulating the data for the combobox seems to reset the input field, which we don't want
}

function afterSelection(){
	setTimeout(function(){ // setTimeout is necessary to avoid strange results due to wijmo/ajax delay 
		console.log("An entry has been selected");
		var Wiki_Link = document.getElementById("Wikipedia_Link");
		Wiki_Link.href = $("#Wikipedia_ComboBox").wijcombobox("option","selectedValue");
		Wiki_Link.innerHTML =  document.getElementById("Wikipedia_ComboBox").value;
	},500);
	
}

document.getElementById("Wikipedia_ComboBox").onkeyup = function (){
	var query = this.value;
	var URL = "https://en.wikipedia.org/w/api.php?format=json&action=opensearch&limit=10&search="+encodeURI(query);
	jsonp(URL,function(data){handleAjaxResponse(data);});
}

ComboBox Issues

ComboBox is not a native HTML input!

A Combobox, as its name might imply, is really a mashup of separate entities; the standard HTML input tag with a “text” type, and the HTML select tag (drop-down selector). Thus, the only real way that a combobox can be created is through Javascript. Luckily, there are several JS UI frameworks that have pre-built comboboxes to use. Here are some nice ones:

HTML has a lot of Cross-Domain Restrictions!

Restrictions on the ability of Javascript and HTML to make network requests and access cross-domain content is very good for the sake of the user; this prevents embedded banner ads from executing malicious attacks and other various attack vectors. Unfortunately, it also makes integrating APIs and third-party tools into a first-party page very tricky.

For example, many sites will let you embed a generated <form> into your page, but only through an iframe. The embedded iframe keeps the source of the original site, which means it has a different domain than your own site that you are embedding it into it. Now, due to the same-origin policy, you can’t interact with it via Javascript. Bummer.

OK, so forms are out as a way to interact with third party content. How about actual HTTP requests? The XMLHttpRequest API is a super easy way to send and receive request via vanilla Javascript and even allows us to do advanced things like specify the request method and whether we are making an asynchronous request or not. Oh wait… the XMLHttpRequest method is also subject to the browser’s cross-domain policy! So, you can make requests to your own domain, but requests to other domains are out, unless they have CORS enabled for your specific domain (which 99% of the time means you are SOL).

Argh… OK, so how about a compromise? What if we use server-side PHP with cURL to make and receive the request, while client side Javascript talks to that PHP page? Success! That will work! Of course, we are basically making our own API on our own domain, which is fine if we are building a first-party system, but for accessing a third-party API/datasource means double the work with double the latency. Another issue is that now instead of request originating from the user’s IP, 100% of request are originating from our server, which without IP rotation, means all from the same IP. Thus, very easy to get either banned or rate-limited access to a third-party API. Not to mention increased use of our bandwidth and server processing resources. However, this is an OK tool for quick and dirty iframe-busting. Otherwise, not a great solution for integrating third-party APIs. Bummer.

Comboboxes and AJAX – More Resources:

Leave a Reply

Your email address will not be published. Required fields are marked *