Подтвердить что ты не робот

Angular JS - Автоматическое фокусирование ввода и отображение раскрывающегося списка typeahead - ui.bootstrap.typeahead

Я использую Angular JS - ui.bootstrap.typeahead:

Я хотел бы нажать кнопку и сфокусировать поле ввода и автоматически показать раскрывающееся меню typeahead. У меня есть директива, которая автоматически фокусирует поле ввода при нажатии кнопки. Как я могу показать выпадающее меню автоматически, чтобы пользователь мог использовать стрелку вниз или щелкнуть, чтобы быстро выбрать пользователя?

Я создал Plunker с JS файлом ui-bootstrap, доступным для редактирования:

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

Это мой полный 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>
4b9b3361

Ответ 1

Как упоминает HarishR в комментарии, еще нет встроенной поддержки этой функции.

Но я просто хочу попробовать взломать и вот результат: http://plnkr.co/edit/Qrnat8yTvISuM1qHHDlA?p=preview

В нем много хаков, чтобы заставить его работать:

  • включить jQuery для использования .trigger(), можно заменить на родной JS, но я ленив.
  • используйте ng-focus для вызова .trigger('input') для запуска всплывающего окна typehead
  • используйте ng-trim = "false", чтобы отключить автоматическое обрезку входного значения.
  • настраиваемая директива empty-typeahead, которая взаимодействует с контроллером ngModel для применения логики secretEmptyKey для обхода проверки типа-head-min-length:

    .directive('emptyTypeahead', function () {
      return {
        require: 'ngModel',
        link: function (scope, element, attrs, modelCtrl) {
          // this parser run before typeahead 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 parser
          modelCtrl.$parsers.push(function (inputValue) {
            return inputValue === secretEmptyKey ? '' : inputValue; // set the secretEmptyKey back to empty string
          });
        }
      }
    })
    
  • настраиваемая функция сравнения фильтров, которая всегда возвращает true (показать все результаты), когда один аргумент является secretEmptyKey:

    $scope.stateComparator = function (state, viewValue) {
      return viewValue === secretEmptyKey || (''+state).toLowerCase().indexOf((''+viewValue).toLowerCase()) > -1;
    };
    
  • удалить ограничение для фильтра, чтобы показать все результаты

  • установить свойства max-height и overflow css для отображения полосы прокрутки, если содержимое слишком длинное

Готово!

Ответ 2

Обновлено:

Я добавил директиву github для упрощения обновлений и доступа. Теперь вы можете установить его как зависимость через беседку.

Оригинальное сообщение:

Я придумал довольно чистый хак, который не требует каких-либо изменений в ui-bootstrap-tpls. Идея заключается в использовании $setViewValue() для запуска всплывающего окна с комбинацией специальной функции компаратора фильтра.

Чтобы обойти проверку minLength, значение $setViewValue() должно быть установлено на значение, превышающее 1, поэтому я использую одну пробельную строку. Роль функции компаратора заключается в том, чтобы рассматривать одно пространство как совпадение со всеми элементами, чтобы они отображались при нажатии на пустой ввод.

Я создал простую директиву:

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;
      };
    }
  };
});

Использование:

<input type="text" ng-model="selected" typeahead="item for item in items | filter:$viewValue:emptyOrMatch | limitTo:8" typeahead-focus >

Ответ 3

Я получил рабочее решение, изменив код в ui-bootstrap-tpls-0.10.0.js. Таким образом, нет различий в разметке html typeahead.

Вы можете посмотреть здесь, http://plnkr.co/edit/LXHDpL?p=preview.

Чтобы использовать это исправление, используйте ui-bootstrap-tpls-0.10.0.js из Plunk. Чтобы увидеть мои изменения, откройте ui-bootstrap-tpls-0.10.0.js из Plunk и найдите "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(' ');
        }
    });

Надеюсь, что это поможет

Ответ 4

Теперь, поскольку у меня недостаточно репутации для комментариев, я должен написать новый ответ, чтобы предупредить людей об ответе runTarm выше. Это жизнеспособное решение, но он рискует столкнуться с следующей ошибкой:

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

Это связано с тем, что ng-focus является синхронизированным событием (см. обсуждение здесь). Вместо этого можно использовать атрибут ng-click-а, и эта ошибка не возникает.

Кроме того, я проверил, что

$element.triggerHandler('input');

работает так же хорошо, как jQuery-trigger в ответе runTarm.

Ответ 5

Кажется, что встроенная поддержка этой функции поступает в предстоящий выпуск в виде атрибута typeahead-min-length, поддерживающего значение 0.

Он реализован в этой фиксации в главной ветке https://github.com/angular-ui/bootstrap/commit/d859f42cc022a5d8779f1c7b358486bbdd04ed57, но с ней еще нет релиза, и это не в 0,14.x.

Надеемся, что новый релиз начнется быстро, так что больше нет необходимости в этих обходах.

Ответ 6

Я хотел что-то вроде описания OP, и единственным решением, которое я нашел, было создание шаблона, который объединяет директивы dropdown и typeahead - возможно, OP или кому-то еще будет полезно:

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>

Ответ 7

Я решил эту проблему через директиву. Когда вы используете эту директиву, показывает список без какого-либо фильтра, тогда вы вводите свой поиск, чтобы найти элемент.

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;
            });

        }
    };
}]);

Я оставляю код представления, чтобы проверить приведенный выше код.

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

Ответ 8

typeahead-min-length = "0" делает трюк (я использую v0.4.0)

Ответ 9

Мне хотелось, чтобы typeahead открывался всякий раз, когда мой элемент ввода имел фокус. Решение @yohairosen не работает для меня в последней версии Angular Bootstrap (Версия: 1.0.3). Вот решение, которое сработало для меня. Он включал ручное обращение к парсеру, прикрепленному с помощью ui-bootstrap-typeahead, который заполняет предложения:

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

Ответ 10

Нам нужно будет trigger ('input') на элементе ввода, когда он сфокусирован.

Правильный способ сделать это в Angular - это сделать это в директиве.

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

Используйте эту директиву для входного элемента typeahead -

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

Ответ 11

вы можете достичь этим кодом

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

измените тест на свой идентификатор типа