I used a combination of things from this thread to work out a solution for what I needed. In my case, I wanted the typeahead field to have the ID value of the item stored on the model as well. IE, I had a table and wanted people to perform application name lookups, but store the ID for that application on a different model field. I'm only posting to help anyone else who might be looking into this type of behavior. I didn't use the angular-ui directive as I didn't want the field to behave like a dropdown if you were tabbing across fields.
This is the HTML:
<tr ng-repeat="item in related_data.decrease" ng-class="{warning: item._action}">
<td>
<input placeholder="account..." type="text" ng-model="item.account" typeahead="accountTypeaheadFunction()" parent="item" key-field="application_id" key-field-target="accountID" class="typeahead-query">
</td>
</tr>
This is the JS code in your controller: [note that I have a custom entity service, so you'd stick whatever you want in there to get the data...]
$scope.accountTypeaheadFunction = function() {
return {
source: function (query, process, event) {
$q.all([
Entity['applications'].query({application_name: '%' + query + '%'}).$q
]).then(function(results) {
objects = [];
map = {};
if( results[0].data.rows ) {
$.each(results[0].data.rows, function (i, accountData) {
map[accountData.application_name] = accountData;
objects.push(accountData.application_name);
});
}
process(objects);
});
}
}
};
Directive:
configModule.directive('typeahead', function($q, Entity) {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, model) {
// Defaults for all type aheads
var defaults = {
updater: function (item ) {
return item;
},
matcher: function (item) {
if (item.toLowerCase().indexOf(this.query.trim().toLowerCase()) != -1) {
return true;
}
},
sorter: function (items) {
return items.sort();
},
highlighter: function (item) {
var regex = new RegExp( '(' + this.query + ')', 'gi' );
return item.replace( regex, "<strong>$1</strong>" );
}
};
// merge the defaults with the passed in source data function
var combinedData = $.extend({}, defaults, scope.$eval(attrs.typeahead));
element.typeahead( combinedData);
element.on('change', function() {
if( attrs.hasOwnProperty('parent') ) {
parent = scope.$eval(attrs.parent);
if( map[element.val()] ) {
parent[ attrs.keyFieldTarget ] = map[element.val()][ attrs.keyField ];
} else {
parent[ attrs.keyFieldTarget ] = "";
}
}
// Update the model/view
scope.$apply(function() {
model.$setViewValue(element.val());
});
});
}
};
});