Repository: bokeh/bokeh

Issue:
make a bokeh TextInput callback responsive as text is typed
This feature is described originally in a Stack Overflow [question,](https://stackoverflow.com/questions/54756780/make-a-bokeh-textinput-callback-responsive-as-text-is-typed) where it was recommended to create a GitHub issue.

Current functionality of the on_change method of a bokeh TextInput object is to run the callback after all text is typed and then some other key is pressed. I want to know if a TextInput object can be configured to run a callback as text is being typed / deleted. The following code is a minimal example.  The application created by the code demonstrates the issue.

    from bokeh.io import curdoc
    from bokeh.layouts import widgetbox, column
    from bokeh.models import TextInput, Button

    button = Button(button_type='success')
    ti = TextInput(title='enter text to enable button')
    layout = column(
        widgetbox(ti),
        widgetbox(button))

    button.disabled = True

    def callback(attr, old, new):
        if ti.value != '':
            button.disabled = False
        else:
            button.disabled = True

    ti.on_change('value', callback)

    curdoc().add_root(layout)



Discussion:
For compatibility this would need to be configurable as a mode of operation (default off)
It'd be nice to have the text input be more configurable generally - so that a user could specify that the event only be triggered when enter key is pressed.
I agree the objective is to develop a generally more configurable TextInput object for bokeh, with default behavior matching current behavior.  

@bryevdv Do you think the bulk of the work to implement this feature will be in javascript or python.  
It's all in Javascript (Typescript) The only thing on the python side is ~1 line to add the new property
@bryevdv I can attempt the work but may get hung up due to a lack of javascript / typescript experience.  Where do I go next?
@russellburdt the first step is getting set up to build BokehJS locally: 

https://bokeh.pydata.org/en/latest/docs/dev_guide/setup.html#devguide-setup

After that, the places to modify are:

* The python class, to add a new `Bool` property:

   https://github.com/bokeh/bokeh/blob/master/bokeh/models/widgets/inputs.py#L86

* The BokehJS class, to add the property and also switch the event listener to "change" vs "input" based on the bool property value

   https://github.com/bokeh/bokeh/blob/master/bokehjs/src/lib/models/widgets/text_input.ts#L30

@russellburdt hi russell! What's the status with this feature? I can help out if you need some javascript help
@mcastanlaris Hi Maria.  Thank you for following up.  I have not yet started on any of Bryan's recommended steps.  My plan is still to contribute to bokeh, starting with this feature, and I will reach out to you with any javascript issues encountered.  
@mcastanlaris Hello and I think the `scripts/dep.py` module can be updated to silence a warning.  When [installing dependencies for conda packages](https://bokeh.pydata.org/en/latest/docs/dev_guide/setup.html#conda-packages), this happens
`$ python scripts/deps.py build
scripts/deps.py:17: YAMLLoadWarning: calling yaml.load() without Loader=... is deprecated, as the default Loader is unsafe. Please read https://msg.pyyaml.org/load for full details.
  meta_src = yaml.load(meta_src.render(load_setup_py_data=load_setup_py_data))
certifi jinja2 nodejs>=8.8 numpy packaging pycurl python-dateutil pyyaml setuptools six tornado>=4.3 yaml`

I was able to get past this warning by modifying line 17 in `scripts/deps.py` - 
`meta_src = yaml.load(meta_src.render(load_setup_py_data=load_setup_py_data), Loader=yaml.FullLoader)`








@mcastanlaris @bryevdv Some progess has been made and I would really appreciate some javascript guidance at this point.  I created the new `Bool` property in the `TextInput` Python class, as you see below.  I also had to add the `Bool` object to the Bokeh imports in `inputs.py`.  I validated none of that broke bokeh by testing with my original [StackOverflow question](https://stackoverflow.com/questions/54756780/make-a-bokeh-textinput-callback-responsive-as-text-is-typed).  The modified `TextInput` class is below.  

At this point I need to modify `text_input.ts` to access the new property and control the callback functionality accordingly, but am not sure how to get started doing that.   

```
class TextInput(InputWidget):
    ''' Single-line input widget.

    '''

    value = String(default="", help="""
    Initial or entered text value.
    """)

    callback = Instance(Callback, help="""
    A callback to run in the browser whenever the user unfocuses the
    ``TextInput`` widget by hitting Enter or clicking outside of the text box
    area.
    """)

    focused = Bool(False, help="""
    set True to execute the callback when the widget is focused as text is
    entered or erased.  default value of False executes the callback only
    when the user unfocuses the ``TextInput`` widget.
    """)

    placeholder = String(default="", help="""
    Placeholder for empty input field
    """)
```

Thank you for the help!
@russellburdt any updates on this? How's the javascript work going and are you blocked on anything specific I can help with?

Resolving patch:
diff --git a/bokeh/models/widgets/inputs.py b/bokeh/models/widgets/inputs.py
--- a/bokeh/models/widgets/inputs.py
+++ b/bokeh/models/widgets/inputs.py
@@ -90,14 +90,20 @@ class TextInput(InputWidget):
     Initial or entered text value.
     """)
 
+    value_input = String(default="", help="""
+    Initial or entered text value that triggers a callback whenever the value changes.
+    """)
+
     callback = Instance(Callback, help="""
     A callback to run in the browser whenever the user unfocuses the
     ``TextInput`` widget by hitting Enter or clicking outside of the text box
     area.
+
+    DEPRECATED: use .js_on_change or .on_change with "value" or "value_input"
     """)
 
     placeholder = String(default="", help="""
-    Placeholder for empty input field
+    Placeholder for empty input field.
     """)
 
 


Test patch: