At first we have found that Typeahead (ui.bootstrap.typeahead) directive fits our needs, but later we run into its limitations.
These are tasks we required to solve:
The second task allows to show some data in popup almost immediately, while to provide more hints lately.
It took us a couple of days to answer both questions. The solution was either to write "typeahead" directive anew, or to write some additional "typeahead" directive to implement missing functionality. We have selected the later.
typeahead
In the additional directive we:
updateSource
Here is the code with small sample:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Typeahead</title> <link type="text/css" rel="stylesheet" href="bootstrap.css" /> <style type="text/css"> .ng-cloak { display: none; } </style> <script src="angular.js"></script> <script src="ui-bootstrap-tpls-0.12.0.js"></script> <script> angular.module("app", ["ui.bootstrap"]). directive( "typeahead", ["$q", function ($q) { return ( { require: 'ngModel', link: function(scope, element, attrs, controller) { /** * @description * Emits "updateSource" event on this scope. * @returns an event. */ scope.updateSource = function() { return scope.$emit("updateSource"); }; scope.$on( "updateSource", function(event) { if (scope != event.targetScope) { return; } if (element.attr("aria-expanded") !== "true") { event.preventDefault(); return; } var value = controller.$modelValue; for(var i = 0; i < controller.$parsers.length; i++) { if (value === undefined) { break; } value = controller.$parsers[i](value); } }); var typeaheadSources = null; var typeaheadValue = null; var typeaheadResult = null; /** * A wrapper of array of sources, where it's assumed that each next * source delivers more data in cost of a longer working time. * * @param {object} value a typeahead hint. * * @param {function(object)} sourcesFn Function receiving typeahead * hint, and returning an array of {Object|Promise}. * If Object is passed rather than Promise then it should contain * following properties: * * - `promise` - `{Promise}` - a result promise. * - `cancel` - `{function()}` - optional function to cancel * the promise. */ scope.sources = function(value, sourcesFn) { var result = typeaheadResult; var prevValue = typeaheadValue; typeaheadValue = controller.$modelValue; typeaheadResult = null; if (result && (prevValue === typeaheadValue)) { return result; } var sources = typeaheadSources; function cancel(count) { for (var i = 0; i < count; ++i) { var source = sources[i]; source.cancel && source.cancel(); } } sources && cancel(sources.length); typeaheadSources = sources = sourcesFn(value); return $q(function(resolve) { sources.forEach(function(source, index) { var promise = source.then ? source : source.promise ? source.promise : $q.when(source); promise.then(function(result) { if (sources != typeaheadSources) { cancel(sources.length); return; } if (element[0] != document.activeElement) { cancel(sources.length); typeaheadSources = null; return; } cancel(index); if (!result || !result.length) { return; } if (resolve) { resolve(result); resolve = null; } else { typeaheadResult = result; if (scope.updateSource().defaultPrevented) { cancel(sources.length); typeaheadSources = null; typeaheadResult = null; typeaheadValue = null; } } }); }); }); } } }); }]). controller( "Test", ["$timeout", function ($timeout) { this.text = null; this.input = null; function source(timeout, count, value) { return ( { promise: $timeout( function() { var result = []; for(var i = 0; i < count; ++i) { result.push({ text: value + " " + i, id: i }); } return result; }, timeout), cancel: function() { $timeout.cancel(this.promise); } }); } // Gets an array of objects in format: // { promise: Promise, cancel: Function } this.suggest = function(value) { return [source(500, 5, value), source(2000, 10, value)]; } }]); </script> </head> <body ng-app="app" ng-controller="Test as test"> <input type="text" ng-model="test.text" typeahead="items.text for items in sources($viewValue, test.suggest)" typeahead-wait-ms="250"/><br /> <input type="text" ng-model="test.input" /> </body> </html>
Demo can be found at typeahead.html, and source at nesterovsky-bros/angularjs-api/bootstrap/typeahead.html.
Remember Me
a@href@title, b, blockquote@cite, em, i, strike, strong, sub, super, u