Skip to content
This repository has been archived by the owner on May 29, 2019. It is now read-only.

Proposition for typeahead : Open suggestion view on focus #764

Closed
jdecool opened this issue Aug 5, 2013 · 74 comments
Closed

Proposition for typeahead : Open suggestion view on focus #764

jdecool opened this issue Aug 5, 2013 · 74 comments

Comments

@jdecool
Copy link
Contributor

jdecool commented Aug 5, 2013

It could be interresting to automatically open the suggestion view when the input field has focus.

It will allow user to see choices it can make when a filter is used and the list is not editable.

What do you think about ?

@pkozlowski-opensource
Copy link
Member

So, I think that there was a brief discussion about something similar. The problem is that semantic of such thing gets very tricky very fast.

One of the main design assumptions of the current typeahead directive is that it should work seamlessly with promises (with any typeahead suggestions to be retrieved asynchronously, usually from a server side). Now if we take this into account the question is - what should be shown in the opened popup? If the input is empty what should we send as a query to the server?

I guess this is not super-common use case for the typeahead directive and as such I would prefer no to add more code / complexity to the directive itself. What I would rather look at is a way of making sure that such functionality can be pluggable. I'm starting to think about refactoring the current directive to use an internal controller so people can plug into the directive.

If I'm not mistaken it should be possible even today to write a simple directive that would listen to the focus event and write a value to the $viewValue - this would trigger the popup window opening.

@jdecool Could you give a separate directive a try? I would be keen to refactor the existing typeahead directive so custom / additional behavior is easy to plug.

@sanfords
Copy link

sanfords commented Aug 6, 2013

I'd like to second the suggestion to show the popup on focus if typeahead-min-length == 0. I've read some of the discussions. Popping up on focus will allow the user to see what kinds of choices are available before they type. Right now there is no way to do this. Adding this capability makes the popup a little more like a traditional <select> popup menu, but not too much. :-) Alternatively, showing the popup on keyboard-arrow-down would also be good, and would be consistent with combo-boxes on PCs.

With your $viewValue idea to show the popup on focus from a directive, what value should be written? Just an empty string? (I'm a little new to all of this.)

(Thank you for your efforts on this and other parts of ui-bootstrap!)

@jdecool
Copy link
Contributor Author

jdecool commented Aug 7, 2013

It's a very interresting idea to allow user to plug an internal controller into the directive to extends functionnalities !

I also agree with @sanfords that displaying suggestion view on focus, is similar to have a typeahead-min-length=0.

Right now, I'm going to try to add a directive to catch the "focus" event.

@sanfords
Copy link

sanfords commented Aug 7, 2013

jdecool, thanks for working on that directive!

I put together two sample apps on another platform to see how a typeahead would "feel" with showing popups on focus. One popped up on focus, and the other popped up on "keyboard down-arrow". I preferred "down-arrow". I guess this could be easily done with the same directive, right?

The select2 control also uses down-arrow to show choices. Has down-arrow become a standard for showing options in typeahead-like controls? At some point this may be what the users expect.

@jdecool
Copy link
Contributor Author

jdecool commented Aug 7, 2013

I have tried to use select2 in my app. The disavantage of the library is that user have to do an action to view type of result. So I try to make an UI which can be use only with keyboard et the most intuitive as possible.

Basic users don't know that they have to press the "down-arrow" to show suggestions. By opening the list on focus, user is guided.

@sanfords
Copy link

sanfords commented Aug 7, 2013

So I think there's a case to be made for both. First is showing the popup on focus: This would be good for, say, a country popup where a one-time user will need to see the choices immediately. Second is the user who doesn't need to see all the options immediately because they have experience with the form, or the typeahead field is one of 10 fields on the page and don't want the popup flashing in front of them as they tab around.

@bkc
Copy link

bkc commented Oct 3, 2013

hello, has there been any more thought/work towards opening the pop-up when minlength=0 and focus event is fired? I really need this.

thanks

@cindoum
Copy link

cindoum commented Oct 7, 2013

To open suggestions on keydown 40

(version 0.6) You can try this workaround : first instruction in link function replaced by
var minSearch = originalScope.$eval(attrs.typeaheadMinLength || '1');

And in the keydown event, add this test before the first one (become if / else if) :
if (scope.matches.length === 0 && evt.which === 40) {
modelCtrl.$setViewValue(modelCtrl.$viewValue);
}

In unshift function, first test become :
if ((inputValue && inputValue.length >= minSearch) || (minSearch === 0))

It wont break anything and will open the suggestion popup when you hit down arrow on an empty input

@sanfords
Copy link

sanfords commented Oct 7, 2013

cindoum, wonderful. This should be in 0.7.

@cindoum
Copy link

cindoum commented Oct 7, 2013

Glad it can help you. I am actually working on adding a few behaviors and fixing some glitchs. If any of them could help people, i might start a branch.

New feature :

  • minLenght = 0 working (done)
  • Open suggestions with button click (to simulate a ) (done) Add a behavior to indicate that no match is found when scope.matches.length == 0 (done) Add option for nullable / not nullable (to simulate null option in ) (in progress)

Fix done :

  • Typeahead highlight filter : Fix regex when matchItem is a Html template (it was highlighting the html tag) (done)

@cindoum
Copy link

cindoum commented Oct 7, 2013

To open it on focus, with the fixed described in my previous post, it should not be harder than adding a directive on the input (like typeahead-open-on-focus) and in the link function bind on element focus and then set modelCtrl.$setViewValue(''); (need require: NgModel).

This directive is describe somewhere else in an issue. (not tested)

@sanfords
Copy link

sanfords commented Oct 7, 2013

When investigating typeaheads a few months ago I realized that everybody has a different opinion what a typeahead should be. :-)

So here's what I personally need from a typeahead:

  1. Show choices on keyboard-down-arrow. (great work!)
  2. Associate an id with each item, and bind the id.
  3. Allow creation of new items if they don't exist.
  4. A button to show the choices.
  5. For the popup to go away when the user tabs/shift-tabs/clicks away.
  6. To make sure the CSS hooks are there so the choices list doesn't fall off the bottom of the window. Some of my typeaheads have hundreds of choices.

I believe 1, 3, 4 are in your list.

@cindoum
Copy link

cindoum commented Oct 7, 2013

Typeahead seam to go in a way : advanced input, for me it is more an advanced select...

Most of what your asking is already possbile.

  1. need a fix, but easily fixable.
  2. this is done with your 'options' kind of -> item.Id as item.Name for item in items
  3. This is more or less what editable is doing, but return a string instead of an object ...
  4. This is a bit harder, need to play with dissmissClickHandler and events... I have a few more tweaks to have it stable, but then i can send you my code if you want.
  5. Tab, enter, esc already close the popup, you can easily add more. Add keyCode to HOT_KEYS line 4 (not tested). Input blur also close the suggestions popup.
  6. Not sure to understand... But if you have hundreds of suggestions, why dont you use a filter limitTo:x ? I think this is a strange behavior to show hundreds of suggestions in one block...

The 3 is not a behavior i will be working on as it seem ok to me. If editable = false, it return the match or null, that is what i expect and if editable = true, it return the match object or the string typed in the input if cannot fit it to matches.

@sanfords
Copy link

sanfords commented Oct 7, 2013

This is great news. There are so many typeheads out there I just wanted to enumerate what I'd like. I think they've all blended together in my mind.

#6 is actually important. Users with large screens can easily see > 30 items in the dropdown. And they'll often want to scroll through the choices before typing. It's especially important combined with #1 and empty input, I think.

@cindoum
Copy link

cindoum commented Oct 7, 2013

  1. A button to show the choices.

I have it working but this is not a clean way to do it, but i need the typeahead working as we expected for yesterday so ill keep this.

Here is what i've done (if it can help people while a cleaner way is found):

  1. Add a parameter to typeahead (toggle in my case) -> typeahead-toggle="show"
  2. Add a button to your html -> (ng-click is the important piece)
  3. In the typeahead directive add this ->
    scope.$watch('show', function (n, o) { if (n != o) { if (scope.matches.length == 0) { element[0].focus(); element[0].select(); getMatchesAsync(element[0].value); } else { scope.select(scope.activeIdx); resetMatches(); } } });
  4. Change the dissmiss click handler function to ->
    var dismissClickHandler = function(evt) { if (element[0] !== evt.target) { if (isInternalClick(evt.target) !== true) { resetMatches(); scope.$digest(); } } };
  5. Add this function ->
                   var elem = angular.element(target);
    
    
            if (elem.hasClass('dropdown') == true || elem.parent().hasClass('dropdown') == true) {
                return true;
            }
    
            return false;
        }```
    
    
    change the 'dropdown' class to any class name you assign to your element (button).
    
    

As i said, this is more of a hack than a new feature. I didnt want to make the toggle button part of the typeahead, so everything is left outside.

EDIT: Sorry for indentation, cannot find proper tag to show code :/

@cindoum
Copy link

cindoum commented Oct 7, 2013

If you dont want to use a limitTo filter, you could also (or both) set a maxHeight with a overflow-y: auto to wrap the suggestion popup with a scroll bar...

I am not sure to understand how you would like it to be? Showing 30+ suggestions, if no maxHeight or any kind of pagination / filtering will alway go down to the page bottom...

@braco
Copy link

braco commented Oct 20, 2013

@cindoum,

minLength = 0 working (done)

would be great to have; it looks like this is requested pretty often, and I'm trying to hack my way through it right now. Do you have this up anywhere?

@sanfords
Copy link

Thank you for your efforts with this. I think these are great additions to typeahead.

@trpriel
Copy link

trpriel commented Nov 7, 2013

I think so also minLength=0 has a lot of added value

@sanfords
Copy link

sanfords commented Nov 7, 2013

Regarding large lists, I don't think it's too much to present hundreds of items to users. It seems kind of the norm, actually. Having hundreds of bound choices though will slow down rendering if there are just a few typeaheads on-screen. Is there a way to either not-bind (or cleverly bind) the list choices or use something like angular-virtual-scroll?

@JohnYoungers
Copy link

I also agree this would be very beneficial functionality.

I have several scenarios where the list of options gets filtered by other inputs, and so it would be helpful to set that minLength property to 0, and allow the user to view the entire list when they get to the input opposed to being required to guess at which of the few values are still applicable.

It sounded like there was a concern with sending off a query of nothing, but I would imagine you'd want the server method call to be limited in how much data it will return in general.

@ada-lovecraft
Copy link

+1

@raykin
Copy link

raykin commented Mar 4, 2014

useful when the options were not huge, so show options when input was focus can save user memory and user can type adhoc strings too.

@swayf
Copy link

swayf commented Mar 9, 2014

+1 is any plans to include the feature? :)

@xtiam57
Copy link

xtiam57 commented May 2, 2014

I just added this in the link function and worked just fine: element.bind('focus', function(ev) { modelCtrl.$setViewValue(' '); });

@paddymul
Copy link

paddymul commented May 5, 2014

I would like typeahead to show all possible completions on click. This is similar to ui-select2. I have some code that accomplishes this, but it needs to be cleaned up.

@mattvague
Copy link

+1

@rvanbaalen
Copy link
Contributor

No more +1 comments please

That way we can be notified about comments that actually need attention instead of being flooded with +1's

In the meanwhile -- All PR's covering this feature are more than welcome

@wesleycho wesleycho added this to the Backlog milestone Aug 2, 2015
@Enykey
Copy link

Enykey commented Aug 6, 2015

Can you update Angular-UI doc to reflex the fact that typeahead-min-data cannot be 0 and should be 1 or more, please?

@icfantv
Copy link
Contributor

icfantv commented Aug 6, 2015

@Enykey please feel free to fork the repo and submit a PR.

@shyamal890
Copy link

This seems to be one of the most sought after feature yet there is no one who's working on it. WHY?!

Moreover, yohairosen(http://stackoverflow.com/users/1270769/yohairosen) has already developed a workaround - http://stackoverflow.com/a/27331340/3955513

@wesleycho
Copy link
Contributor

@shyamal890 please exercise patience/professionalism - maintaining a large open source project is difficult, and we have many issues that we have to manage with limited resources. Showing impatience will not get us to consider this faster, and only adds noise since instead of working through the backlog of issues, we are being distracted improperly - we are doing the best we can to get through our backlog of issues and systematically address them, and adding noise will only slow down the project more.

@thesalami
Copy link

Thank you @chenyuzhcy for answering a 2 year awaited question with < 10 lines of code!
We all look forward to the next release.

@kuroky360
Copy link

+1

@gandaldf
Copy link

I created a directive that embeds the Typeahead directive and mimics the functionality of a combobox/select:

.directive('combobox', ['$compile', '$timeout', function($compile, $timeout) {
var directive = {
    restrict: 'E',
    scope: {
        model: "=",
        item: "@",
        items: "=i",
        onSelect: "&",
        onBlur: "&"
    },
    compile: compile
};

return directive;

function compile(element, attrs) {
    return function(scope, element, attrs) {
        scope.focusing = false;
        scope.first = null;

        scope.open = function() {
            var ctrl = element.find('input').controller('ngModel');

            var temp = scope.model;

            ctrl.$setViewValue(' ');

            $timeout(function() {
                ctrl.$setViewValue('');
                scope.model = temp;
            });
        };

        scope.select = function(item, model, label) {
            scope.focusing = true;
            scope.onSelect({item: item, model: model, label: label});
        };

        scope.blur = function() {
            scope.focusing = false;
            scope.onBlur();
        };

        scope.focus = function() {
            if (scope.focusing === false) {
                scope.focusing = true;
                element.find('input')[0].focus();
                scope.first = scope.model;
                scope.open();
            }
        };

        scope.keyup = function(key) {
            if (key === 27) {
                scope.model = scope.first;
                scope.open();
            }
        };

        var template = '<div class="combo combo-static">' +
                       '    <input ng-model="model"' +
                       '           uib-typeahead="' + attrs.item + '"' +
                       '           typeahead-focus-first="false"' +
                       '           typeahead-min-length="0"' +
                       '           typeahead-editable="false"' +
                       '           typeahead-on-select="select($item, $model, $label)"' +
                       '           ng-focus="focus()"' +
                       '           ng-blur="blur()"' +
                       '           ng-keyup="keyup($event.keyCode)"' +
                       '           placeholder="' + attrs.placeholder + '"' +
                       '           type="text"' +
                       '           class="' + attrs.class + '">' +
                       '    <div class="combo-label" ng-click="focus()" ng-hide="focusing">' +
                       '        <span>{{model || "' + attrs.placeholder + '"}}</span>' +
                       '        <i class="fa fa-caret-down"></i>' +
                       '    </div>' +
                       '</div>';

        element.html(template);
        element.removeClass(attrs.class);

        $compile(element.contents())(scope);
    };
}
}]);

Here is my plunk: http://plnkr.co/edit/zWN950IxOndlYQHBXdY9?p=preview
Hope it could help someone.

wesleycho pushed a commit that referenced this issue Jan 6, 2016
- Fix issue where programmatic focus on typeahead input does not work

Closes #5150
Fixes #764
@santosh-k1
Copy link

Its Easy to add only $scope.selected = ' '; in your controller.. i have checked with ui-bootstrap-tpls-0.10.0.js and its working fine.

@icfantv
Copy link
Contributor

icfantv commented Feb 1, 2016

@santosh-k1, I think you may have gotten that version number wrong. We're currently at version 1.1.1.

@lin-credible
Copy link

+1

@icfantv
Copy link
Contributor

icfantv commented Feb 25, 2016

@lin-credible, please do not just add a +1 to issues. It adds no intrinsic value to the discussion and serves to only clutter up the thread. Thanks.

@mrdulin
Copy link

mrdulin commented Apr 14, 2016

@santosh-k1 work for me ! Thanks!
You must init the ng-model with a "" not null

@copiali
Copy link

copiali commented Sep 14, 2016

Any update for this feature? Need it when we need to always show a default option on focus before trigger any search......

@santosh-k1
Copy link

@copiali below code will help you to resolve the issue.. it has open the value on text box focus:
<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" />


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

      $scope.open = function() {
        $scope.opened = true;
      }
      $scope.close = function() {
        $scope.opened = false;
      }
    });
}());

Working Demo Link : Demo http://plnkr.co/edit/bZMEOx0Qwo6VzW7oSuEE?p=preview

@Ablablab
Copy link

+1

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests