I am trying to create an reusable typehead directive in AngularJS but I've hit a few bumps on the road. Here is my working example:
Directives.js
app.directive('autosuggest', function() {
return {
restrict: 'A',
link: function (scope, elem, attrs) {
/* Nothing here yet */
}
};
});
app.directive('suggestinput', function() {
return {
restrict: 'A',
link: function (scope, elem, attrs) {
// Bind keys
elem.bind('keydown', function (e) {
if (e.keyCode == 38 || e.keyCode == 40 || e.keyCode == 13) { scope.$emit('listNavigate', { code: e.keyCode }); }
else {
// Get suggestions
scope.getSuggest(attrs.source, elem.val());
}
});
// Listen for suggestion list
scope.$on('listSelect', function (e, data) {
elem.val(data.name);
});
}
};
});
app.directive('suggestlist', function() {
var selectedIndex = -1;
return {
restrict: 'A',
link: function (scope, elem, attrs) {
scope.$on('listNavigate', function (e, data) {
if (data.code === 38) {
selectedIndex--;
// User pressed up arrow
elem.children().removeClass('sel');
elem.children().eq(selectedIndex).addClass('sel');
} else if(data.code == 40) {
selectedIndex++;
// User pressed down arrow
elem.children().removeClass('sel');
elem.children().eq(selectedIndex).addClass('sel');
} else if(data.code == 13) {
// Prepare data
var selectData = {};
selectData.suggestValue = elem.children().eq(selectedIndex).children().html();
selectData.suggestId = elem.children().eq(selectedIndex).children().attr('item-id');
// Send data to input(s)
scope.$emit('listSelect', selectData);
}
});
}
};
});
Html:
<fieldset autosuggest>
<input type="hidden" name="languageId">
<input type="text" suggestinput source="languages">
<input type="submit" class="button" value="Save">
<ul suggestlist ng-show="suggest.languages" class="suggestList">
<li ng-repeat="language in suggest.languages">
</li>
</ul>
</fieldset>
I have two questions:
How do I pass the item-id from the suggestion list to the hidden input?
How to make it a reusable component? (make it work even if I have multiple typeheads on the same page)
Thanks!
Have you taken a look # Angular ui's directive for twitter bootstrap typehead. This'll save you a lot of hassle ui.bootstrap.typeahead
HTML
<div ng-app="app" id="app">
<div class='container-fluid' ng-controller="TypeaheadCtrl">
<pre>Model: {{selected| json}}</pre>
<input type="text" ng-model="selected" typeahead="state for state in states | filter:$viewValue | limitTo:8"/>
</div>
</div>
JS/Controller
var app = angular.module('app', ['ui.bootstrap']);
function TypeaheadCtrl($scope) {
$scope.selected = undefined;
$scope.states = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Dakota', 'North Carolina', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'];
}
JSFIDDLE
Related
I'm new to JS but have good knowledge in C and HTML and want to create a search box so that when I write something, a drop-down list appears with some results. I already have the code to create the animated search field (which is not mine) and the code to create this search filter list but I don't know how to "connect" them.
Here is the JS part for the search bar filter: (To use it you just need this html code
<input type="text" id="searchInput" placeholder="Search article"/> here is the fiddle: https://jsfiddle.net/c30L5esq/)
$("#searchInput").on('keyup', function() {
var searchValue = $(this).val().toUpperCase();
if(searchValue.length > 0) {
searchAndFilter(searchValue);
} else {
$("#articlesearch li").hide();
}
});
function searchAndFilter(searchTerm) {
$("#articlesearch li").each(function() {
var currentText = $(this).text().toUpperCase();
if (currentText.indexOf(searchTerm) >= 0) {
$(this).show();
} else {
$(this).hide();
}
});
}
$(document).ready(function() {
$("#articlesearch li").hide();
});
And here it is the link for the search bar fullscreen animation :
https://codepen.io/suez/pen/obpLvy/
And what I want is that when I use this search bar animation, I can actually search through a list and it will show me results that match what I typed in a dropdown.
Thank you !
$(document).ready(function() {
var list = $("#articlesearch>li");
list.hide();
$(document).on('input', "#searchInput", function() {
/* searchAndFilter($(this).val()) */
list.hide();
var searchTerm = ($(this).val() + '').toUpperCase();
if (searchTerm.length > 0) {
list.each(function() {
var currentText = $(this).text().toUpperCase();
if (currentText.indexOf(searchTerm) >= 0) {
$(this).show();
} else {
$(this).hide();
}
});
}
});
});
Jsfiddle: https://jsfiddle.net/ravinila/ghovnzty/13
I think you are looking for is autocomplete for search input. For that we have to use typeahead.js plugin. You can go with the link on the plugin and Codepen example link. There are multiple example demos in the link.
var substringMatcher = function(strs) {
return function findMatches(q, cb) {
var matches, substringRegex;
// an array that will be populated with substring matches
matches = [];
// regex used to determine if a string contains the substring `q`
substrRegex = new RegExp(q, 'i');
// iterate through the pool of strings and for any string that
// contains the substring `q`, add it to the `matches` array
$.each(strs, function(i, str) {
if (substrRegex.test(str)) {
matches.push(str);
}
});
cb(matches);
};
};
var states = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California',
'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii',
'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana',
'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota',
'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire',
'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'North Dakota',
'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island',
'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont',
'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'
];
$('#the-basics .typeahead').typeahead({
hint: true,
highlight: true,
minLength: 1
},
{
name: 'states',
source: substringMatcher(states)
});
<div id="the-basics">
<input class="typeahead" type="text" placeholder="States of USA">
</div>
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.jquery.min.js"></script>
What I want to do is this: when a user starts typing in the typehead input and no matches are found it should show a message saying "no match found".
Below is the code and a plunker:
HTML
<html ng-app="ui.bootstrap.demo">
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-animate.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-2.0.2.js"></script>
<script src="example.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<script type="text/ng-template" id="customTemplate.html">
<a>
<img ng-src="http://upload.wikimedia.org/wikipedia/commons/thumb/{{match.model.flag}}" width="16">
<span ng-bind-html="match.label | uibTypeaheadHighlight:query"></span>
</a>
</script>
<div class='container-fluid typeahead-demo' ng-controller="TypeaheadCtrl">
<h4>Custom templates for results</h4>
<pre>Model: {{customSelected | json}}</pre>
<input type="text" ng-model="customSelected" placeholder="Custom template" uib-typeahead="state as state.name for state in statesWithFlags | filter:{name:$viewValue}" typeahead-template-url="customTemplate.html" class="form-control" typeahead-show-hint="true" typeahead-min-length="0">
</div>
</body>
</html>
JavaScript
angular.module('ui.bootstrap.demo', ['ngAnimate', 'ui.bootstrap']);
angular.module('ui.bootstrap.demo').controller('TypeaheadCtrl', function($scope, $http) {
var _selected;
$scope.selected = undefined;
$scope.states = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Dakota', 'North Carolina', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'];
// Any function returning a promise object can be used to load values asynchronously
$scope.getLocation = function(val) {
return $http.get('//maps.googleapis.com/maps/api/geocode/json', {
params: {
address: val,
sensor: false
}
}).then(function(response){
return response.data.results.map(function(item){
return item.formatted_address;
});
});
};
$scope.ngModelOptionsSelected = function(value) {
if (arguments.length) {
_selected = value;
} else {
return _selected;
}
};
$scope.modelOptions = {
debounce: {
default: 500,
blur: 250
},
getterSetter: true
};
$scope.statesWithFlags = [{'name':'Alabama','flag':'5/5c/Flag_of_Alabama.svg/45px-Flag_of_Alabama.svg.png'},{'name':'Alaska','flag':'e/e6/Flag_of_Alaska.svg/43px-Flag_of_Alaska.svg.png'},{'name':'Arizona','flag':'9/9d/Flag_of_Arizona.svg/45px-Flag_of_Arizona.svg.png'},{'name':'Arkansas','flag':'9/9d/Flag_of_Arkansas.svg/45px-Flag_of_Arkansas.svg.png'},{'name':'California','flag':'0/01/Flag_of_California.svg/45px-Flag_of_California.svg.png'},{'name':'Colorado','flag':'4/46/Flag_of_Colorado.svg/45px-Flag_of_Colorado.svg.png'},{'name':'Connecticut','flag':'9/96/Flag_of_Connecticut.svg/39px-Flag_of_Connecticut.svg.png'},{'name':'Delaware','flag':'c/c6/Flag_of_Delaware.svg/45px-Flag_of_Delaware.svg.png'},{'name':'Florida','flag':'f/f7/Flag_of_Florida.svg/45px-Flag_of_Florida.svg.png'},{'name':'Georgia','flag':'5/54/Flag_of_Georgia_%28U.S._state%29.svg/46px-Flag_of_Georgia_%28U.S._state%29.svg.png'},{'name':'Hawaii','flag':'e/ef/Flag_of_Hawaii.svg/46px-Flag_of_Hawaii.svg.png'},{'name':'Idaho','flag':'a/a4/Flag_of_Idaho.svg/38px-Flag_of_Idaho.svg.png'},{'name':'Illinois','flag':'0/01/Flag_of_Illinois.svg/46px-Flag_of_Illinois.svg.png'},{'name':'Indiana','flag':'a/ac/Flag_of_Indiana.svg/45px-Flag_of_Indiana.svg.png'},{'name':'Iowa','flag':'a/aa/Flag_of_Iowa.svg/44px-Flag_of_Iowa.svg.png'},{'name':'Kansas','flag':'d/da/Flag_of_Kansas.svg/46px-Flag_of_Kansas.svg.png'},{'name':'Kentucky','flag':'8/8d/Flag_of_Kentucky.svg/46px-Flag_of_Kentucky.svg.png'},{'name':'Louisiana','flag':'e/e0/Flag_of_Louisiana.svg/46px-Flag_of_Louisiana.svg.png'},{'name':'Maine','flag':'3/35/Flag_of_Maine.svg/45px-Flag_of_Maine.svg.png'},{'name':'Maryland','flag':'a/a0/Flag_of_Maryland.svg/45px-Flag_of_Maryland.svg.png'},{'name':'Massachusetts','flag':'f/f2/Flag_of_Massachusetts.svg/46px-Flag_of_Massachusetts.svg.png'},{'name':'Michigan','flag':'b/b5/Flag_of_Michigan.svg/45px-Flag_of_Michigan.svg.png'},{'name':'Minnesota','flag':'b/b9/Flag_of_Minnesota.svg/46px-Flag_of_Minnesota.svg.png'},{'name':'Mississippi','flag':'4/42/Flag_of_Mississippi.svg/45px-Flag_of_Mississippi.svg.png'},{'name':'Missouri','flag':'5/5a/Flag_of_Missouri.svg/46px-Flag_of_Missouri.svg.png'},{'name':'Montana','flag':'c/cb/Flag_of_Montana.svg/45px-Flag_of_Montana.svg.png'},{'name':'Nebraska','flag':'4/4d/Flag_of_Nebraska.svg/46px-Flag_of_Nebraska.svg.png'},{'name':'Nevada','flag':'f/f1/Flag_of_Nevada.svg/45px-Flag_of_Nevada.svg.png'},{'name':'New Hampshire','flag':'2/28/Flag_of_New_Hampshire.svg/45px-Flag_of_New_Hampshire.svg.png'},{'name':'New Jersey','flag':'9/92/Flag_of_New_Jersey.svg/45px-Flag_of_New_Jersey.svg.png'},{'name':'New Mexico','flag':'c/c3/Flag_of_New_Mexico.svg/45px-Flag_of_New_Mexico.svg.png'},{'name':'New York','flag':'1/1a/Flag_of_New_York.svg/46px-Flag_of_New_York.svg.png'},{'name':'North Carolina','flag':'b/bb/Flag_of_North_Carolina.svg/45px-Flag_of_North_Carolina.svg.png'},{'name':'North Dakota','flag':'e/ee/Flag_of_North_Dakota.svg/38px-Flag_of_North_Dakota.svg.png'},{'name':'Ohio','flag':'4/4c/Flag_of_Ohio.svg/46px-Flag_of_Ohio.svg.png'},{'name':'Oklahoma','flag':'6/6e/Flag_of_Oklahoma.svg/45px-Flag_of_Oklahoma.svg.png'},{'name':'Oregon','flag':'b/b9/Flag_of_Oregon.svg/46px-Flag_of_Oregon.svg.png'},{'name':'Pennsylvania','flag':'f/f7/Flag_of_Pennsylvania.svg/45px-Flag_of_Pennsylvania.svg.png'},{'name':'Rhode Island','flag':'f/f3/Flag_of_Rhode_Island.svg/32px-Flag_of_Rhode_Island.svg.png'},{'name':'South Carolina','flag':'6/69/Flag_of_South_Carolina.svg/45px-Flag_of_South_Carolina.svg.png'},{'name':'South Dakota','flag':'1/1a/Flag_of_South_Dakota.svg/46px-Flag_of_South_Dakota.svg.png'},{'name':'Tennessee','flag':'9/9e/Flag_of_Tennessee.svg/46px-Flag_of_Tennessee.svg.png'},{'name':'Texas','flag':'f/f7/Flag_of_Texas.svg/45px-Flag_of_Texas.svg.png'},{'name':'Utah','flag':'f/f6/Flag_of_Utah.svg/45px-Flag_of_Utah.svg.png'},{'name':'Vermont','flag':'4/49/Flag_of_Vermont.svg/46px-Flag_of_Vermont.svg.png'},{'name':'Virginia','flag':'4/47/Flag_of_Virginia.svg/44px-Flag_of_Virginia.svg.png'},{'name':'Washington','flag':'5/54/Flag_of_Washington.svg/46px-Flag_of_Washington.svg.png'},{'name':'West Virginia','flag':'2/22/Flag_of_West_Virginia.svg/46px-Flag_of_West_Virginia.svg.png'},{'name':'Wisconsin','flag':'2/22/Flag_of_Wisconsin.svg/45px-Flag_of_Wisconsin.svg.png'},{'name':'Wyoming','flag':'b/bc/Flag_of_Wyoming.svg/43px-Flag_of_Wyoming.svg.png'}];
});
Here is the demo in plunker.
Use typeahead-no-results. Add the following to your input
typeahead-no-results="noResults"
Then add the following, or similar, below.
<div ng-show="noResults">
<i class="glyphicon glyphicon-remove"></i> No Match Found
</div>
I've amended your Plunkr here
I am using the same code from the typeahead example here. However if you type in a period . the dropdown will appear with matches. Is all this code above the array definition necessary? Is there an easier way to accomplish the same thing where you won't get matches for periods if there are no periods in your array?
I see the bloodhound example below it accomplishes the same thing and doesn't match periods but is it using var states to define an array and then using var states to construct a new bloodhound engine is this a mistake?
Copied code from the website:
var substringMatcher = function(strs) {
return function findMatches(q, cb) {
var matches, substringRegex;
// an array that will be populated with substring matches
matches = [];
// regex used to determine if a string contains the substring `q`
substrRegex = new RegExp(q, 'i');
// iterate through the pool of strings and for any string that
// contains the substring `q`, add it to the `matches` array
$.each(strs, function(i, str) {
if (substrRegex.test(str)) {
matches.push(str);
}
});
cb(matches);
};
};
var states = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California',
'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii',
'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana',
'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota',
'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire',
'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'North Dakota',
'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island',
'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont',
'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'
];
$('#the-basics .typeahead').typeahead({
hint: true,
highlight: true,
minLength: 1
},
{
name: 'states',
source: substringMatcher(states)
});
In my case I am generating an array of tags so instead of the states I am using var tags = <%= raw Tag.all.pluck(:name) %>;
You may find indexOf to be more useful than a regex for your application.
if (haystack.indexOf(needle) != -1) {
/* needle is in haystack */
}
or, case insensitively:
if (haystack.toLowerCase().indexOf(needle.toLowerCase()) != -1) {
}
something like this:
function findMatches(q, cb) {
var lq = q.toLowerCase();
var matches = [];
$.each(strs, function(i, str) {
if (str.toLowerCase().indexOf(lq) != -1) matches.push(str);
});
cb(matches);
}
So regular expressions are "complicated", and easy to get wrong. "." for instance has special meaning in regex which matches any charachter except newline in JS, so for example based on Mozilla's docs RexExp(".n", "i") would match ".n", "an", "in", "?n" and many more.
Regular expressions are difficult to get right.
#John-Hascall's suggestion is a good fix for the problem.
I have a list of states (Florida, Alabama ...) and I want to create named anchors above the first occurance of the first letter.
Letter Links
<nav>
<ul class="letters">
<li ng-repeat="letter in locations.getLetters()">
{{letter}}
</li>
</ul>
</nav>
States
<nav>
<ul class="locations">
<li ng-repeat="state in locations.states">{{state.state}}
<a ng-if="" id="{{state.state.charAt(0)}}">fgsdf</a>
<ul>
<li ng-repeat="city in state.cities">
<a href>{{city.name}}</a>
</li>
</ul>
</li>
</ul>
</nav>
I am stuck at <a ng-if="" id="{{state.state.charAt(0)}}">fgsdf</a>
I have tried ng-if="!document.getElementById(state.state.charAt(0))" and that doesn't work. Does anyone have any suggestions as to how I should go about this?
Update
I've considered using angular's built-in filters to filter the states in ngRepeat. When a user clicks A, only the states starting with A should show. This seems like a much cleaner and more intuitive approach and will improve UX.
You can try this approach
let's assume you have the input as a simple array of strings. before placing it in the controller, we can group states by the first letter of each state using a simple object (the letter is a key, the value is an array of strings)
http://jsfiddle.net/q2y93ym7/1/
html:
<body ng-app="HelloApp" ng-controller="Controller" class="container">
<div class="well">
{{letter}}
</div>
<div ng-attr-id="{{letter}}" ng-repeat="(letter,states) in letters">
<h1>{{letter}}</h1>
<h2 ng-repeat="state in states">{{state}}</h2>
<hr>
</div>
</body>
js:
angular.module('HelloApp', [])
.controller('Controller', function ($scope) {
var states = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyomin'];
// Let's prepare the input
var letters = $scope.letters = {};
states.forEach(function (state) {
var letter = state.charAt(0);
if (!letters[letter]) {
letters[letter] = [];
}
letters[letter].push(state);
});
})
EDIT:
As #DRobinson says, nothing guarantees keys of an Object will be sorted. therefore you can try using this great approach / instead an Object, use an array
added <h1>{{letter}}</h1>, thanks #Tony
That won't work because document is not part of the current scope.
Here's what you can do:
Controller:
$scope.elementExists = function (selector) {
return typeof $document.getElementById(selector) !== 'undefined';
}
Html:
<a ng-if="!elementExists('elementId')">Link</a>
Don't forget to add $document as a dependency of your controller.
I am using bootstrap typeahead in my angular project. My requirement was to open the typeahead drop down by pressing down key when user have not entered any text. I have successfully added this functionality by using this link. Here is my demo.
Now down key opens the drop down and hence it lose the default behavior of traversing the drop down (moving down to next option).
index.html
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.2/angular.js"></script>
<script src="ui-bootstrap-tpls-0.10.0.js"></script>
<script src="script.js"></script>
</head>
<body>
<div class="container-fluid" ng-controller="TypeaheadCtrl">
<input type="text" ng-keydown="show($event)" ng-trim="false" ng-model="selected" empty-typeahead typeahead="state for state in states | filter:$viewValue:stateComparator" class="form-control" />
<pre ng-show="opened">Model: {{selected | json}}</pre>
</div>
</body>
</html>
script.js
(function () {
var secretEmptyKey = '[$empty$]'
angular.module('plunker', ['ui.bootstrap'])
.directive('emptyTypeahead', function () {
return {
require: 'ngModel',
link: function (scope, element, attrs, modelCtrl) {
// this parser run before typeahead's parser
modelCtrl.$parsers.unshift(function (inputValue) {
var value = (inputValue ? inputValue : secretEmptyKey); // replace empty string with secretEmptyKey to bypass typeahead-min-length check
modelCtrl.$viewValue = value; // this $viewValue must match the inputValue pass to typehead directive
return value;
});
// this parser run after typeahead's parser
modelCtrl.$parsers.push(function (inputValue) {
return inputValue === secretEmptyKey ? '' : inputValue; // set the secretEmptyKey back to empty string
});
}
}
})
.controller('TypeaheadCtrl', function($scope, $http, $timeout) {
$scope.selected = undefined;
$scope.states = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Dakota', 'North Carolina', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'];
$scope.stateComparator = function (state, viewValue) {
return viewValue === secretEmptyKey || (''+state).toLowerCase().indexOf((''+viewValue).toLowerCase()) > -1;
};
$scope.show = function (e) {
var keyCode = e.keyCode || e.which;
if (keyCode == 40) { //If it's the down key
$timeout(function () {
$(e.target).triggerHandler('input');
});
}
};
});
}());
Is there any way to open the drop down when clicking first time and then move to next option if clicked again?
Finally, I fix the issue by putting an if condition when opening typeahead drop down.
$scope.show = function (e) {
if($scope.selected === undefined){
var keyCode = e.keyCode || e.which;
if (keyCode == 40) { //If it's the down key
$timeout(function () {
$(e.target).triggerHandler('input');
});
}
}
};
and giving undefined in $scope.selected if user have not selected any item:
$scope.clearIfEmpty = function () {
if($scope.selected !== undefined && $scope.selected.length === 0){
$scope.selected = undefined;
}
}
Fix in action