twitter / typeahead.js

typeahead.js is a fast and fully-featured autocomplete library
http://twitter.github.io/typeahead.js/
MIT License
16.52k stars 3.2k forks source link

Feature request: Prevent changing input value on cursorchanged #884

Open ragulka opened 10 years ago

ragulka commented 10 years ago

I have a use case where I do not want to change the input value when moving up/down with arrow keys in the suggestion list. However, it seems that currently this is not possible - it's hardcoded in Typeahead and I cannot override it in any way from outside?

jQuery UI Autocomplete has a similar feature - when you return false in the focus event handler, it does not change the input value.

ragulka commented 10 years ago

As a temporary workaround - is there a way to override/monkey-patch the _cursorMoved handler of Typeahead?

jharding commented 10 years ago

As a temporary workaround - is there a way to override/monkey-patch the _cursorMoved handler of Typeahead?

Nope, not that I can think of.

eggsurplus commented 10 years ago

@ragulka For monkey patching, I'm using a similar method that @ttimbul uses here: https://github.com/twitter/typeahead.js/issues/800. Works well. If you do need to use any of the methods you'll just need to replace them as they are defined in the lib. (e.g. .bind becomes $.proxy)

eggsurplus commented 10 years ago

And now that I just ran into the same need you had I gave up and just hardcoded the desire behavior. Wasn't worth spending several more hours on finding a way.

jhuesos commented 10 years ago

+1 we need this one as well

jhuesos commented 10 years ago

I came up with a dirty fix that seems to work fine. Although I am aware that since I am accessing private methods and replacing them, I will have to be careful whenever there is a new version of typeahead to check if still works.

var typeaheadInputElement = $('#autosuggest-input');
var typeaheadInstance = typeaheadInputElement.data("ttTypeahead");
var cursorMovedFn = typeaheadInstance.dropdown._callbacks.cursorMoved.sync[0];
var context = typeaheadInstance.dropdown;

// Proxy to keep the value when cursor changes
var proxy = function () {
  var copyOfValue = typeaheadInputElement.val();  
 // Call original method onCursorMoved()
  cursorMovedFn.apply(context, arguments);    
  typeaheadInputElement.val(copyOfValue);
}

// Replacing original method with our proxy
typeaheadInstance.dropdown._callbacks.cursorMoved.sync[0] = proxy;

This solution what it does is to replace the original method with a proxy. What we do is to make a copy of the value, call the original method that will replace the value in the input field, and once that call is done we will set the previous value.

This plunkr I created show this fix on action:

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

Go to script.js to see the code. As mentioned, this is a super-hacky-solution that probably is very fragile. I am planning to add more defensive code to detect changes in the internal Typeahead API.

I was thinking if you guys have a better suggestion to do this. Do you? The biggest problem is the plugin makes almost everything private so I cannot extend the plugin to add custom behavior in a clean way.

jharding commented 10 years ago

If this option were to get added, what would be a good name for it?

zhuston commented 9 years ago

+1 It is tough to come up with a name I really like. ideas: replaceInput (true, false), autocomplete (true, false)

In my use case I am navigating to another page when a user selects a suggestion. The suggestion text just doesn't make sense to be replaced into the input box.

4rthem commented 9 years ago

+1

simplenotezy commented 9 years ago

@jharding mimickCursor: true|false, dynamicInputText: true|false, changeInputValue:true|false

I need this as well

yosefw commented 9 years ago

+1

joxx commented 9 years ago

+1 that is what i need. Thanks

kamranayub commented 9 years ago

This should probably also apply to selecting with a mouse; basically, if this option is enabled, it will not change the input value at all.

fredericgrati commented 9 years ago

+1

hustlzp commented 9 years ago

+1

kamranayub commented 9 years ago

You can clone my PR #1256 if you need this, I've been using in production for a few months now.

oskariot commented 7 years ago

Since this issue is not yet closed nor is #1256 merged I'd like to present the workaround I've come up recently. The trick is to add the query string to every data record and set displayKey to match the field name. Then on typehead:cursorchanged the current query value will be copied to the input field so in fact it will stay unchanged. You can achieve this by adding transform function parameter to prefetch or remote of Bloodhound:

  1. First set displayKey: 'q' in your dataset options.
  2. Then (assuming that you are using jQuery) add this to prefetch or remote (depends which one of them you are using)
transform: function(response) {
  return response.map(function(el) {
    var query = $('#your_typehead').val();
    return $.extend(el, { q: query });
  });
};

if you are using typehead without Bloodhound (method described here) modify your source function like this:

source: function(query, syncResults, asyncResults) {
    return $.get("/search?query=" + query, function(data) {
      query = $('#your_typehead').val();
      return asyncResults(data.map(function(obj) {
        return $.extend(obj, { q: query });
      }));
    });
  }

Hope it helps!