angularjsangular-ui-bootstraptypeaheadangular-ui-typeahead

AngularJS [typeahead] reopen result dropdown on onFocus


I have problem in following use case in typeahead:

Problem:

So, Moral of the story is that in first case string should get cleared if it is not selected from list and it is substring of list string...

So, maybe a typeahead-search-on-focus setting?

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

I have this Plunker to show how my code looks like.

There is also GitHub Issue.


Solution

  • Hello Everyone,

    After some efforts i got the proper solution...

    There are some directives that can be useful for the solution,

    Add above condition in typeaheadFocus directive,

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

    Add following function in typeaheadFocus directive and typeaheadOnDownArrow directive,

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

    Change this condition in ui-bootstrap.js,

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

    Add following event in 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("");
       }
    });
    

    Add this in 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;
            }
        );
    

    Add the above function with ui-bootstrap.js in all directive,

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

    Example:

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

    Hope this answer will be helpful for you.

    Here I have added ui-bootstrap.js