AngularJS [typeahead] повторно открыть раскрывающийся список результатов на onFocus

У меня проблема со следующим вариантом использования в typeahead:

Проблема:

  • пользователь начинает печатать, раскрывающийся список открывается и показывает результаты
  • пользователь щелкает из поля ввода (из поля ввода ничего не удаляется), раскрывающийся список закрывается
  • пользователь щелкает обратно в поле ввода с опережением ввода (и НЕ начинает ничего вводить), и ничего не происходит. → желаемое поведение: раскрывающийся список открывается снова и показывает тот же список результатов, что и в прошлый раз (конечно, это происходит только в том случае, если в поле ввода есть что-то)
  • Когда пользователь ищет что-либо, и если результат найден и происходит потеря фокуса, и строка не очищается снова, фокус ничего не происходит, никаких подсказок
  • Когда пользователь что-либо ищет, и если результат не найден и происходит потеря фокуса, строка очищается, а также появляется сообщение об отсутствии данных.

Итак, мораль этой истории заключается в том, что в первом случае строка должна быть очищена, если она не выбрана из списка и является подстрокой строки списка ...

Итак, может быть, настройка поиска с опережением по фокусу?

HTML:

<input type="text" focus-me="opened" ng-focus="onFocus($event)" ng-show="opened" ng-trim="false" ng-model="selected" empty-typeahead typeahead="state for state in states | filter:$viewValue:stateComparator" class="form-control" />

JS:

(function () {
var secretEmptyKey = '[$empty$]'

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();
                  });
              }
          });
      }
  };
})
.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', 'Wyoming'];
  $scope.opened = true;
  
  $scope.stateComparator = function (state, viewValue) {
    return viewValue === secretEmptyKey || (''+state).toLowerCase().indexOf((''+viewValue).toLowerCase()) > -1;
  };
  
  $scope.onFocus = function (e) {
    $timeout(function () {
      $(e.target).trigger('input');
      $(e.target).trigger('change'); // for IE
    });
  };
});
}());

У меня есть Plunker, чтобы показать, как выглядит мой код.

Также существует проблема с GitHub.


person Sagar Naliyapara    schedule 28.07.2016    source источник


Ответы (2)


Вам нужно проверить размытие, существует ли входное значение в списке или нет. Для этого сделайте следующее:

Добавить атрибут ng-blur="onBlur($event)" во ввод

И определите в своем контроллере следующий метод:

$scope.onBlur = function (e) {
    $timeout(function () {
    var val = $(e.target).val();
    if(val && $scope.states.indexOf(val) == -1) {
      $(e.target).val("")
    }
  });
};
person Rathvi Bharat    schedule 15.08.2016

Всем привет,

После некоторых усилий я нашел правильное решение ...

Есть несколько директив, которые могут быть полезны для решения,

Добавьте указанное выше условие в директиву typeaheadFocus,

    if ((e.type == "focus" && viewValue != ' ') && (e.type == "focus" && viewValue != '')){
        return;
    }

Добавьте следующую функцию в директиву typeaheadFocus и директиву typeaheadOnDownArrow,

    //compare function that treats the empty space as a match
    scope.$emptyOrView = function(actual) {
        if(ngModel.$viewValue.trim()) {
            return actual ? actual.value.toString().toLowerCase().indexOf(ngModel.$viewValue.toLowerCase()) > -1 : false;
        }
        return true;
    };            

Измените это условие в ui-bootstrap.js,

  if (inputFormatter) {
     locals.$model = modelValue;
     if(modelValue){
     locals.$label = view_value;
  } else {
     locals.$label = '';
  }

Добавьте следующий event в ui-bootstrap js,

element.bind('blur', function(evt) {
   hasFocus = false;
   if (!isEditable && modelCtrl.$error.editable) {
      element.val('');
      modelCtrl.$viewValue = ' ';
   }
   if(!isEditable && scope.no_data_found) {
         //ngModel.$viewValue = '';
         modelCtrl.$viewValue = ' ';
         scope.no_data_found = false;
         popUpEl.find('.typeaheadNoData').remove();
         resetMatches();
         scope.$digest();
         element.val("");
   }
});

Добавьте это в ui-bootstrap.js,

        // I listen for emptyTypeAhead events from the parent scope chain.
    originalScope.$on(
        "emptyTypeAhead",
        function handlePingEvent( event, newVal1) {
            var scope = originalScope.$new();
            modelCtrl.$setViewValue(newVal1);
            return newVal1;
        }
    );

Добавьте указанную выше функцию с ui-bootstrap.js во все директивы

.directive('shouldFocus', function($parse) {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            scope.$watch(attrs.shouldFocus, function(newVal, oldVal) {
                var isActive = scope.$eval(attrs.shouldFocus);
                if(isActive) {
                    var ele = element[0];
                    var parent = ele.parentNode

                    if(ele.offsetTop + ele.offsetHeight > parent.offsetHeight + parent.scrollTop){
                        parent.scrollTop = ele.offsetTop;
                    } else if(parent.scrollTop > ele.offsetTop) {
                        parent.scrollTop = ele.offsetTop;
                    }
                }
            });
        }
    }
});

Пример:

<input type="text" ng-model="inquiry.account" 
 placeholder="Select Account" 
 typeahead="account.id as account.text for account in account_typeahead_json" 
 typeahead-input-formatter="formatModel($model,$label)" 
 typeahead-editable="false" typeahead-on-down-arrow typeahead-focus />

Надеюсь, этот ответ будет вам полезен.

Здесь я добавил ui-bootstrap.js

person Sagar Naliyapara    schedule 17.09.2016