Display Unique Results within dijit.form.ComboBox

By  on  

The Dojo Toolkit does well in allowing easy integration of web services with Dijit widgets via dojo.store.JsonRest.  One such widget is the dijit.form.ComboBox, which uses data stores to displays an autosuggest pane populated with items based on the store's contents.  One problem I've run into is that some services return duplicate labels (i.e. two persons named "David Walsh").  You want both items if you can use both of them but what if you're using ComboBox for search?  You wouldn't want duplicates.  That's where I created a small custom class to prevent duplicates within the ComboBox's dropdown menu.

Custom Dojo Class

dijit.form.ComboBox provides a dropDownClass parameter which allows you to customize which class should handle dropdown duties.  The default class is dijit.form._ComboBoxMenu.  We will extend dijit.form._ComboBoxMenu and for our custom duplicate-preventing class, then change the dropDownClass parameter for any instance of ComboBox where we don't want duplicates.

The method that handles items before placing them into the dropdown is the createOptions method. createOptions cycles through the results and creates the HTML for them.  What we need to do is "intercept" the results before they can be added to the dropdown.  Let's create our custom _UniqueComboBoxMenu class with intercept capabilities:

// Provide the class
dojo.provide("davidwalsh.form._UniqueComboBoxMenu");

// Get dependencies
dojo.require("dijit.form.ComboBox");

// Declare the class
dojo.declare("davidwalsh.form._UniqueComboBoxMenu", dijit.form._ComboBoxMenu, {
	
	createOptions: function(results, dataObject, labelFunc) {
		
		// Cycle through to find uniques
		var uniqueKeys = {}, uniqueItems = [];
		dojo.forEach(results,function(result,index) {
			var label = labelFunc(result);
			if(typeof label != "string") {
				label = label.label;
			}
			if(!uniqueKeys[label]) {
				uniqueKeys[label] = result;
				uniqueItems.push(result);
			}
		});
		
		// Update arguments arr
		arguments[0] = uniqueItems;
		
		// Call inheritance chain for this method, return result
		return this.inherited(arguments);
	}
});

Our custom createOptions method receives the options, dataObject, and labelFunc, a function which generates the item's label text/HTML.  Before any other processing, we cycle through each item, create its label, and store the label in an object so that we know when it's been used.  If the label hasn't been used yet, we add the option to the uniqueItems array.

Once the uniques have been created, we push them into the original arguments array's first position, then call this.inherited(arguments) to call the method's original functionality.  In plain English, all we're doing is stripping duplicates from the results array and carrying on as usual.  This technique is less evasive than directly editing the existing class and, as you can see, our custom class stays small.

Implementing the Class

Implementing the class is simple.  Since ComboBox provides the dropDownClass option, all you need to do is provide our custom class to each instance that should implement it:

var combo = new dijit.form.ComboBox({
	store: myDataStore
	dropDownClass: "davidwalsh.form._UniqueComboBoxMenu"
},"myNode");

The instance above will only return unique suggestions!

This post should highlight one aspect of Dojo that I love -- extreme flexibility and the ability to create tiny classes thanks to the awesome inheritance pattern provided by dojo.declare().  Keep this class handy if you use Dijit forms and web services!

Recent Features

  • By
    Being a Dev Dad

    I get asked loads of questions every day but I'm always surprised that they're rarely questions about code or even tech -- many of the questions I get are more about non-dev stuff like what my office is like, what software I use, and oftentimes...

  • By
    Camera and Video Control with HTML5

    Client-side APIs on mobile and desktop devices are quickly providing the same APIs.  Of course our mobile devices got access to some of these APIs first, but those APIs are slowly making their way to the desktop.  One of those APIs is the getUserMedia API...

Incredible Demos

  • By
    Parallax Sound Waves Animating on Scroll

    Scrolling animations are fun. They are fun to create and fun to use. If you are tired of bootstrapping you might find playing with scrolling animations as a nice juicy refreshment in your dry front-end development career. Let's have a look how to create animating...

  • By
    Detect Vendor Prefix with JavaScript

    Regardless of our position on vendor prefixes, we have to live with them and occasionally use them to make things work.  These prefixes can be used in two formats:  the CSS format (-moz-, as in -moz-element) and the JS format (navigator.mozApps).  The awesome X-Tag project has...

Discussion

  1. rajkamal

    can this be done like this.

    pseudo code:

    createOptions: function(results, dataObject, labelFunc) {
    isResultsItmesUnique(results) ? your_implementation : return labelFunc(result)
    }

    will this be efficient?

  2. Hi David,

    I was happy to find this rare gem of a code snippet that works with a dijit. I rewrote this unique combobox result using AMD, and was surprised how well it just worked. I’ve posted my updated code in a Github Gist at https://gist.github.com/raykendo/6418dbc9750b22e2069c.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!