Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular JS - Automatically focus input and show typeahead dropdown - ui.bootstrap.typeahead

I am using Angular JS - ui.bootstrap.typeahead:

I would like to click a button and focus an input field and automatically show the typeahead suggestion dropdown. I have a directive that automatically focuses the input field when the button is clicked. How can I show the dropdown automatically so the user can use the down arrow, or click, to quickly choose a user?

I have created a Plunker with the ui-bootstrap JS file editable for tinkering:

http://plnkr.co/edit/Z79LY0OYlwFc3wirjxol?p=preview

This is my full script:

<!doctype html> <html ng-app="plunker">   <head>     <link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">     <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.2/angular.js"></script>     <script src="ui-bootstrap-tpls-0.10.0.js"></script>   </head>   <body>  <script>   angular.module('plunker', ['ui.bootstrap'])   .directive('focusMe', function($timeout, $parse) {     return {         //scope: true,   // optionally create a child scope         link: function(scope, element, attrs) {             var model = $parse(attrs.focusMe);             scope.$watch(model, function(value) {                 if(value === true) {                      $timeout(function() {                         element[0].focus();                      });                 }             });          }     }; }); function TypeaheadCtrl($scope, $http) {    $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.opened = false;    $scope.open = function() {     $scope.opened = true;   }   $scope.close = function() {     $scope.opened = false;   } }  </script> <div class='container-fluid' ng-controller="TypeaheadCtrl">      <h4>How can I open the typeahead dropdown automatically when button is pressed?</h4>     <p>I have a directive that automatically focuses on the field but I can't seem to automatically show the typeahead. Even adding down arrow key click support would be great.      <br/><br/>      <button class="btn btn-default" ng-show="!opened" ng-click="open()">Open Input and show typeahead!</button>     <button class="btn btn-default" ng-show="opened" ng-click="close()">Close Input</button>     <br/><br/>      <input type="text"     focus-me="opened"     ng-show="opened"     ng-model="selected"      typeahead="state for state in states | filter:$viewValue | limitTo:8"      class="form-control">       <br/>     <pre ng-show="opened">Model: {{selected | json}}</pre>   </div>   </body> </html> 
like image 433
Holland Risley Avatar asked Jul 15 '14 17:07

Holland Risley


2 Answers

Updated:

I added the directive to github for easy updates and access. You can now install it as a dependency through bower.

Original post:

I came up with a pretty clean hack that does not require any changes to ui-bootstrap-tpls. The Idea is to use $setViewValue() to trigger the popup with a combination of a special filter comparator function.

In order to bypass the minLength check, $setViewValue() has to be set to a value longer than 1 so i'm using one space string. The role of the comparator function is to treat one space as a match to all items so they'll all show up when clicking on an empty input.

I created a simple directive:

angular.module('app') .directive('typeaheadFocus', function () {   return {     require: 'ngModel',     link: function (scope, element, attr, ngModel) {        //trigger the popup on 'click' because 'focus'       //is also triggered after the item selection       element.bind('click', function () {          var viewValue = ngModel.$viewValue;          //restore to null value so that the typeahead can detect a change         if (ngModel.$viewValue == ' ') {           ngModel.$setViewValue(null);         }          //force trigger the popup         ngModel.$setViewValue(' ');          //set the actual value in case there was already a value in the input         ngModel.$setViewValue(viewValue || ' ');       });        //compare function that treats the empty space as a match       scope.emptyOrMatch = function (actual, expected) {         if (expected == ' ') {           return true;         }         return actual.indexOf(expected) > -1;       };     }   }; }); 

Usage:

<input type="text" ng-model="selected" typeahead="item for item in items | filter:$viewValue:emptyOrMatch | limitTo:8" typeahead-focus > 
like image 154
yohairosen Avatar answered Oct 05 '22 08:10

yohairosen


As HarishR mentioned in a comment, there is no built-in support for this feature yet.

But I just want to try hacking around and here is the result: http://plnkr.co/edit/Qrnat8yTvISuM1qHHDlA?p=preview

It contains a lot of hacks to make it works:

  1. include jQuery in order to use .trigger(), could be replace with native JS but I'm lazy.
  2. use ng-focus to call .trigger('input') for triggering the typehead popup
  3. use ng-trim="false" to disable input's value auto trimming
  4. a custom empty-typeahead directive that interact with the ngModel's controller for applying the secretEmptyKey logic to bypass typeahead-min-length check:

    .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       });     }   } }) 
  5. a custom filter comparator function that always return true (show all results) when one argument is the secretEmptyKey:

    $scope.stateComparator = function (state, viewValue) {   return viewValue === secretEmptyKey || (''+state).toLowerCase().indexOf((''+viewValue).toLowerCase()) > -1; }; 
  6. remove the limitTo filter to show all results

  7. set max-height and overflow css properties to show scrollbar if content is too long

Done!

like image 29
runTarm Avatar answered Oct 05 '22 08:10

runTarm