web-dev-qa-db-fra.com

Angular JS - Concentre automatiquement la saisie et affiche le menu déroulant typeahead - ui.bootstrap.typeahead

J'utilise Angular JS - ui.bootstrap.typeahead:

Je voudrais cliquer sur un bouton et concentrer un champ de saisie et afficher automatiquement la liste déroulante des suggestions de frappe. J'ai une directive qui concentre automatiquement le champ de saisie lorsque le bouton est cliqué. Comment puis-je afficher la liste déroulante automatiquement afin que l'utilisateur puisse utiliser la flèche vers le bas ou cliquer pour choisir rapidement un utilisateur?

J'ai créé un Plunker avec le fichier JS ui-bootstrap modifiable pour le bricolage:

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

Voici mon script complet:

<!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>
34
Holland Risley

Comme HarishR l'a mentionné dans un commentaire, il n'y a pas encore de support intégré pour cette fonctionnalité.

Mais je veux juste essayer de pirater et voici le résultat: http://plnkr.co/edit/Qrnat8yTvISuM1qHHDlA?p=preview

Il contient beaucoup de hacks pour le faire fonctionner:

  1. inclure jQuery afin d'utiliser .trigger (), pourrait être remplacé par JS natif mais je suis paresseux.
  2. utilisez ng-focus pour appeler .trigger ('input') pour déclencher la popup typehead
  3. utilisez ng-trim = "false" pour désactiver le recadrage automatique de la valeur d'entrée
  4. une directive vide-typeahead personnalisée qui interagit avec le contrôleur du ngModel pour appliquer la logique secretEmptyKey pour contourner la vérification typeahead-min-length:

    .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. une fonction de comparaison de filtre personnalisée qui renvoie toujours true (afficher tous les résultats) lorsqu'un argument est secretEmptyKey:

    $scope.stateComparator = function (state, viewValue) {
      return viewValue === secretEmptyKey || (''+state).toLowerCase().indexOf((''+viewValue).toLowerCase()) > -1;
    };
    
  6. supprimer le filtre limitTo pour afficher tous les résultats

  7. définir les propriétés css max-height et overflow pour afficher la barre de défilement si le contenu est trop long

Terminé!

21
runTarm

Mis à jour:

J'ai ajouté la directive à github pour des mises à jour et un accès faciles. Vous pouvez maintenant l'installer en tant que dépendance via bower.

Poste d'origine:

J'ai trouvé un hack assez propre qui ne nécessite aucune modification de ui-bootstrap-tpls. L'idée est d'utiliser $ setViewValue () pour déclencher le popup avec une combinaison d'une fonction spéciale de comparateur de filtre.

Afin de contourner la vérification minLength, $ setViewValue () doit être défini sur une valeur supérieure à 1, donc j'utilise une chaîne d'espace. Le rôle de la fonction de comparateur est de traiter un espace comme une correspondance avec tous les éléments afin qu'ils apparaissent tous lorsque vous cliquez sur une entrée vide.

J'ai créé une directive simple:

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 >
55
yohairosen

J'ai obtenu une solution de travail en changeant du code dans ui-bootstrap-tpls-0.10.0.js. Il n'y a donc aucune différence dans le balisage html typeahead.

Vous pouvez voir ici http://plnkr.co/edit/LXHDpL?p=preview .

Pour utiliser ce correctif, utilisez le ui-bootstrap-tpls-0.10.0.js du Plunk. Pour voir mes modifications, ouvrez ui-bootstrap-tpls-0.10.0.js depuis le Plunk et recherchez 'ahneo'.

 1. //minimal no of characters that needs to be entered before typeahead
    kicks-in
    // ahneo :: before
    //var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;
    // ahneo :: after (changed minimal no of characters to 0 by default)
    var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 0;
 2. // ahneo :: new (set input value to empty string if it contains " " string value)
    if (inputValue === ' ') {
        inputValue = '';
        modelCtrl.$setViewValue('');
    }  
 3. // ahneo :: before
    //if (inputValue && inputValue.length >= minSearch) {
    // ahneo :: after (add new condition to get matches for min search = 0)
    if (minSearch === 0 || inputValue && inputValue.length >= minSearch) {
 4. // ahneo :: new (bind element to focus event to trigger modelCtrl.$parsers.unshift method)
    element.bind('focus', function (evt) {
        if (modelCtrl.$viewValue === '') {
            modelCtrl.$setViewValue(' ');
        }
    });

J'espère que cela t'aides

12
Henry Neo

Maintenant, comme je n'ai pas assez de réputation pour commenter, je dois écrire une nouvelle réponse pour avertir les gens de la réponse de runTarm ci-dessus. Il s'agit d'une solution viable, mais elle court le risque de rencontrer l'erreur suivante:

Error: [$rootScope:inprog] $apply already in progress

Cela semble être dû au fait que ng-focus est un événement synchronisé ( voir la discussion ici ). Au lieu de cela, on peut utiliser l'attribut ng-click, et cette erreur ne se produit pas.

De plus, j'ai vérifié que

$element.triggerHandler('input');

fonctionne aussi bien que le jQuery-trigger dans la réponse de runTarm.

6
Lyckberg

Il semble que la prise en charge intégrée de cette fonctionnalité soit disponible dans une prochaine version sous la forme de typeahead-min-length attribut prenant en charge la valeur 0.

Il est implémenté dans ce commit dans la branche master https://github.com/angular-ui/bootstrap/commit/d859f42cc022a5d8779f1c7b358486bbdd04ed57 , mais il n'y a pas encore de version avec cela et ce n'est pas dans la 0.14. branche x.

Espérons qu'une nouvelle version arrivera rapidement afin qu'il n'y ait plus besoin de ces solutions de contournement.

5
Cvuorinen

Je voulais quelque chose comme la description de l'OP et la seule solution que j'ai trouvée était de proposer un modèle qui combine les directives déroulantes et typeahead - peut-être que l'OP ou quelqu'un d'autre le trouvera utile:

angular.module('app', ['ui.bootstrap'])
.controller('AppCtrl', function($scope) {
  $scope.model;
  $scope.options = [{label:'Option 1'}, {label:'Option 2'}, {label:'Option 3'}];
  
  $scope.onSelect = function($item, $model, $label) {
    $scope.model = angular.copy($item);
  }
});
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet"/>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.12.0/ui-bootstrap-tpls.min.js"></script>

<div ng-app='app' style='padding:10px'>
  <div ng-controller='AppCtrl'>
    <div class='dropdown' dropdown style='width:200px'>
      <!-- This needs UI Bootstrap 0.12 to work -->
      <div class='input-group'>
        <input type='text' class='form-control' ng-model='model.label' typeahead="op.label for op in options | filter:$viewValue | limitTo:8" typeahead-editable='false' />
        <span class='input-group-btn'>
          <button class='btn btn-default dropdown-toggle' dropdown-toggle>
            <span class='caret'></span>
          </button>
        </span>
      </div>
      <ul class="dropdown-menu" role='menu' style='max-height:200px;overflow-y:scroll'>
        <li ng-repeat='op in options'>
          <a href ng-click='onSelect(op)'>{{op.label}}</a>
        </li>
      </ul>
    </div>
  </div>
</div>

Bien sûr, vous pouvez le simplifier pour que les options ne soient qu'un tableau de chaînes - je les ai faites des objets parce que c'était plus comme ce dont j'avais besoin.

4
spongessuck

typeahead-min-length = "0" fait l'affaire (j'utilise v0.4.0)

3
cnuis2cool

J'ai résolu ce problème grâce à une directive. Lorsque vous utilisez cette directive, affiche la liste sans filtre puis, vous tapez votre recherche pour trouver un élément.

angular.module('myapp')
.directive('typeaheadLikeSelect', 
['$parse',function($parse) {
    return {
        require: 'ngModel',
        link: function (scope, element, attr, ngModel){

            var aux_modelValue, aux_viewValue,
                modelGetter = $parse(attr.ngModel),
                modelSetter = modelGetter.assign;

            var noViewValue = function(){
              return
              ngModel.$$lastCommittedViewValue === undefined ||
              !ngModel.$$lastCommittedViewValue.trim();
            };

            var forceEvent = function(){
              ngModel.$setViewValue();
              ngModel.$viewValue = ' ';
              ngModel.$setViewValue(' ');
              ngModel.$render();
              scope.$apply();
              element.val(element.val().trim());
            };

            element.on('mousedown', function(e){
              e.stopPropagation();
              forceEvent();
            });

            element.on('blur', function(e){
              e.stopPropagation();
              if(aux_modelValue){
                modelSetter(scope, aux_modelValue);
                scope.$apply();
              }
            });

            scope.$watch(function () {
              return ngModel.$modelValue;
            }, function(newValue, oldValue){
              if(newValue || (!newValue && !oldValue))
                aux_modelValue = newValue;
            });

        }
    };
}]);

Je laisse un code de vue, pour tester le code ci-dessus.

<script type="text/ng-template" id="customTemplate.html">
      <a>
          <span ng-bind-html="match.label.description | uibTypeaheadHighlight:query"></span>
      </a>
</script>
    <div class="form-group has-feedback" ng-class="{'has-success':items.mymodel}">
            <input
                placeholder="typeahead"
                ng-model="items.mymodel"
                uib-typeahead="responses as responses for responses in getResponses($viewValue)"
                typeahead-template-url="customTemplate.html"
                typeahead-input-formatter="$model.description"
                typeahead-loading="loadingResponses"
                typeahead-no-results="noResponses"
                typeahead-editable="false"
                typeahead-on-select="changeItem($item)"
                class="form-control"
              required="required"
              typeahead-like-select>
              <div ng-show="noResponses">
                <i class="glyphicon glyphicon-remove"></i> No Results Found
              </div>

              <span ng-show="!items.mymodel" class="glyphicon glyphicon-search form-control-feedback" aria-hidden="true"></span>
              <span ng-show="items.mymodel" class="glyphicon glyphicon-ok form-control-feedback" aria-hidden="true"></span>
          </div>
2

Je voulais que la tête de frappe s'ouvre chaque fois que mon élément d'entrée avait le focus. La solution de @ yohairosen n'a pas fonctionné pour moi sur la dernière version de Angular Bootstrap (Version: 1.0.3). Voici la solution qui a fonctionné pour moi.) Cela impliquait d'invoquer manuellement l'analyseur attaché par ui-bootstrap-typeahead qui remplit les suggestions:

angular.module('app')
.directive('typeaheadFocus', function () {
  return {
      require: 'ngModel',
      link: function (scope, element, attr, ngModel) {
        element.bind('click', function () {
          ngModel.$parsers[0](ngModel.$viewValue);
        });
      }
    };
  };
});

Cela peut être bogué car il suppose que l'analyseur ajouté par ui-bootstrap-typeahead est le seul.

0
jake

vous pouvez réaliser par ce code

 $scope.change = function() {
        var e = document.getElementById("test");
        var $e = angular.element(e);
        $e.triggerHandler('focus');
        $e.triggerHandler('input');
    }

changer le test en votre ID de type

0
User_3535

Ce que nous voulons, c'est trigger ('input') sur l'élément d'entrée lorsqu'il est focalisé.

La façon correcte de le faire dans Angular est de le faire dans une directive.

angular.module('app')    
.directive('showList', function() {
   return {
       restrict: 'A',
       link: function(scope, iEle) {
           iEle.focus(function() {
               iEle.trigger('input');
           });
       }
   };
});

Utilisez cette directive sur l'élément d'entrée typeahead -

<input show-list uib-typeahead="state for state in states | filter:$viewValue" typeahead-min-length="0" class="form-control">
0
Rahul Kumar