AngularJS Autocomplete Directive with Label and Value Model Properties

With AngularJS, we can use the JQuery UI Autocomplete widget (details here) to convert any text box to an autocomplete control which can either call a web service or use an object array as data source.  The limitation with this widget is that it only knows of the one control associated to the directive which means that we can only set either the label or value.

To illustrate this limitation, let’s look at a simple customer search with autocomplete implementation in which we just type names starting with the letter O.

Problem Description:

As we type the client information, we can see that the widget automatically shows a list of names matching what has been entered. When a name is selected from the list, it is assigned to the autocomplete text box which is bound to a model property (client.name), but for more practical purposes, we would also like to get the id.  We can also note the header label only shows the information that was typed, but not the selected name. There is also a problem with updating the view.


Since for most common cases we need the behavior to be similar to a Select control which has both a Label and Option value properties, we need to add a hidden input field and set the ng-model attribute to the model property, client.id. We are now set to capture the client name and id in two different fields. The problem still remains that we need to extend the autocomplete functionality, so that we are able to set the client.id property on the form.

To address this challenge in a simple approach, we can add a callback on the controller scope which can be used by the directive to callback and set any additional properties.  Let’s take a look at a simple implementation of this approach:

The controller has a new scope function (setCliendData) which is available to the directive via the injected scope variable.  This function just sets the controller model with the selected option. The change on the directive is to implement the select event handler which is raised after every selection from the list. We should note that we need to call the controller function using scope.$apply to force a digest cycle which updates the view with the new model value.  If this is not done, the view would not get updated, and our header would not display the client id and name properly.

What to Note:

The autocomplete widget by default uses the properties value or label to build the list. If those properties are not found, a list with empty names is displayed. To address this, on the directive autocomplete source handler, we map the name property to the value (or label) property that is expected by the autocomplete widget.

We should note that this approach works by sharing the controller scope with the directive. When using isolated scope on the directive, the calls to the dataSource and setCliendData would not be available on the directive, and this would need to be implemented differently.  

I hope this provides some help on how to address some of the limitations when building autocomplete functionality on your apps.


What do you think?