Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
210 lines (181 sloc) 5.59 KB
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Typeahead</title>
<style type="text/css">.ng-cloak { display: none; }</style>
<link type="text/css" rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" />
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.13.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>
You can’t perform that action at this time.