onChange event not working on Streamlit 1.34
onChange event is not working after Streamlit 1.34 release.
@okld is this repo still mantained?
we love your library and use it extensively throughout our projects Unfortunately this issue prevents us from upgrading Streamlit 😣
Thanks! 😀
Bump
Bump x2 @okld it would be great if you could update it, Thank You.
Bump @okld please!
Hello!
This issue is related to the refactoring of streamlit in using custom components.
It is possible to restore the previous functionality by replacing in the file /streamlit_elements/core/callback.py:
# from streamlit.components.v1 import components - OLD
from streamlit.components.v1 import custom_component as components - NEW
However, I hope the streamlit developers will fix this issue in future releases.
Thanks @KhabarovaNina a lot!
I made a small script to patch it automatically (needed for our Docker builds), hope it helps other people :)
import os
import streamlit_elements
import re
def patch_streamlit_elements():
# issue: https://github.com/okld/streamlit-elements/issues/35
relative_file_path = 'core/callback.py'
library_root = list(streamlit_elements.__path__)[0]
file_path = os.path.join(library_root, relative_file_path)
# Read broken file
with open(file_path, 'r') as file:
lines = file.readlines()
broken_import = 'from streamlit.components.v1 import components'
fixed_import = 'from streamlit.components.v1 import custom_component as components\n'
# Fix broken import line
for index, line in enumerate(lines):
if re.match(broken_import, line):
print(f'Replaced broken import in {file_path}')
lines[index] = fixed_import
# Update broken file with fix
with open(file_path, 'w') as file:
file.writelines(lines)
if __name__ == "__main__":
patch_streamlit_elements()
vikvikvr
This is awesome, vikvikvr, thank you very much, the script still works great! 👍
Hello everyone! Problem with onChange event is back in Streamlit 1.40.0 and fix import is not enough now :( If somebody fix it in new version - please, write solution here
This code works in Streamlit 1.40.0.
def patch_modules_streamlit_elements(file: str, old_line: str, new_line: str):
import streamlit_elements
import os
relative_file_path = "core/callback.py"
library_root = list(streamlit_elements.__path__)[0]
file_path = os.path.join(library_root, relative_file_path)
with open(file_path, "r") as file:
lines = file.readlines()
is_changed = False
for index, line in enumerate(lines):
if old_line in line:
print(f"Replacing line {index + 1} in {file_path}")
lines[index] = line.replace(old_line, new_line)
is_changed = True
if is_changed:
with open(file_path, "w") as file:
file.writelines(lines)
import importlib
importlib.reload(streamlit_elements)
return True
def patch_streamlit_elements():
# fix 1.34.0
patch_modules_streamlit_elements(
"core/callback.py",
"from streamlit.components.v1 import components",
"from streamlit.components.v1 import custom_component as components\n",
)
#fix 1.40.0
patch_modules_streamlit_elements(
"core/callback.py",
' user_key = kwargs.get("user_key", None)\n',
"""
try:
user_key = None
new_callback_data = kwargs[
"ctx"
].session_state._state._new_session_state.get(
"streamlit_elements.core.frame.elements_frame", None
)
if new_callback_data is not None:
user_key = new_callback_data._key
except:
user_key = None
""".rstrip()
+ "\n",
)
if __name__ == "__main__":
patch_streamlit_elements()
@bonajoy Thank you so much!
This code works in Streamlit 1.40.0.
def patch_modules_streamlit_elements(file: str, old_line: str, new_line: str): import streamlit_elements import os relative_file_path = "core/callback.py" library_root = list(streamlit_elements.__path__)[0] file_path = os.path.join(library_root, relative_file_path) with open(file_path, "r") as file: lines = file.readlines() is_changed = False for index, line in enumerate(lines): if old_line in line: print(f"Replacing line {index + 1} in {file_path}") lines[index] = line.replace(old_line, new_line) is_changed = True if is_changed: with open(file_path, "w") as file: file.writelines(lines) import importlib importlib.reload(streamlit_elements) return True def patch_streamlit_elements(): # fix 1.34.0 patch_modules_streamlit_elements( "core/callback.py", "from streamlit.components.v1 import components", "from streamlit.components.v1 import custom_component as components\n", ) #fix 1.40.0 patch_modules_streamlit_elements( "core/callback.py", ' user_key = kwargs.get("user_key", None)\n', """ try: user_key = None new_callback_data = kwargs[ "ctx" ].session_state._state._new_session_state.get( "streamlit_elements.core.frame.elements_frame", None ) if new_callback_data is not None: user_key = new_callback_data._key except: user_key = None """.rstrip() + "\n", ) if __name__ == "__main__": patch_streamlit_elements()
You sir are a LEGEND. Unfortunately this isnt working for me. Running python 3.11.0 and streamlit 1.40.0. This is what the callback file looks like for me and it isn't working.
@TheUkrainian1991 After the modification, you need to restart your streamlit application.
@bonajoy Everything is normal, Thank you very much!
@TheUkrainian1991 After the modification, you need to restart your streamlit application.
I have done. New terminal with new virtual environment same issue.
Anyone got it working for 1.44.0?
Anyone got a fix for 1.46.0?
This code works in Streamlit 1.40.0.
def patch_modules_streamlit_elements(file: str, old_line: str, new_line: str): import streamlit_elements import os
relative_file_path = "core/callback.py" library_root = list(streamlit_elements.__path__)[0] file_path = os.path.join(library_root, relative_file_path) with open(file_path, "r") as file: lines = file.readlines() is_changed = False for index, line in enumerate(lines): if old_line in line: print(f"Replacing line {index + 1} in {file_path}") lines[index] = line.replace(old_line, new_line) is_changed = True if is_changed: with open(file_path, "w") as file: file.writelines(lines) import importlib importlib.reload(streamlit_elements) return Truedef patch_streamlit_elements(): # fix 1.34.0 patch_modules_streamlit_elements( "core/callback.py", "from streamlit.components.v1 import components", "from streamlit.components.v1 import custom_component as components\n", )
#fix 1.40.0 patch_modules_streamlit_elements( "core/callback.py", ' user_key = kwargs.get("user_key", None)\n', """ try: user_key = None new_callback_data = kwargs[ "ctx" ].session_state._state._new_session_state.get( "streamlit_elements.core.frame.elements_frame", None ) if new_callback_data is not None: user_key = new_callback_data._key except: user_key = None """.rstrip() + "\n", )if name == "main": patch_streamlit_elements()
You are our saviour sir, thank you sincerely! Tested on both 1.38.0 and 1.46.1 and this script is able to fix the issue for both of the versions! Thank you sincerely again
Anyone got a fix for 1.46.0?
the above code is working fine (just tested with 1.46.1 and it seems working for me) Just dont forget to close your streamlit app beforehand and restart after the patch is complete
what version of python is everyone using with a working fix?
Hello Everyone,
I followed the above
This code works in Streamlit 1.40.0.
def patch_modules_streamlit_elements(file: str, old_line: str, new_line: str): import streamlit_elements import os
relative_file_path = "core/callback.py" library_root = list(streamlit_elements.__path__)[0] file_path = os.path.join(library_root, relative_file_path) with open(file_path, "r") as file: lines = file.readlines() is_changed = False for index, line in enumerate(lines): if old_line in line: print(f"Replacing line {index + 1} in {file_path}") lines[index] = line.replace(old_line, new_line) is_changed = True if is_changed: with open(file_path, "w") as file: file.writelines(lines) import importlib importlib.reload(streamlit_elements) return Truedef patch_streamlit_elements(): # fix 1.34.0 patch_modules_streamlit_elements( "core/callback.py", "from streamlit.components.v1 import components", "from streamlit.components.v1 import custom_component as components\n", )
#fix 1.40.0 patch_modules_streamlit_elements( "core/callback.py", ' user_key = kwargs.get("user_key", None)\n', """ try: user_key = None new_callback_data = kwargs[ "ctx" ].session_state._state._new_session_state.get( "streamlit_elements.core.frame.elements_frame", None ) if new_callback_data is not None: user_key = new_callback_data._key except: user_key = None """.rstrip() + "\n", )if name == "main": patch_streamlit_elements()
Hello Sir @bonajoy, @Ege-BULUT, The callback in the following example still doesn't work for me though I made the corresponding change in callback.py as suggested in the thread. I used Streamlit 1.46.1 (also tried 1.50.0) and python 3.13.3. Do you have a repo that's working so I can refer to? I would really appreciate any help and advice!
from streamlit_elements import elements, mui, sync
import streamlit as st
if "my_text" not in st.session_state:
st.session_state.my_text = ""
with elements("sync_example"):
# The onChange event triggers the sync() callback, which
# stores the text field's value in st.session_state.my_text
mui.TextField(
label="Type something...",
defaultValue=st.session_state.my_text,
onChange=sync("my_text")
)
mui.Typography(f"Session State says: {st.session_state.my_text}")
@calvinflorecruit @Ege-BULUT
Everyone!
Success for Streamlit version 1.50! Whoohoo!
I've been wracking my head against this for 3 days straight.
Finally found the fix:
https://github.com/streamlit/streamlit/pull/8633
The guy said this:
"raethlein commented on May 8, 2024 • Describe your changes In the past, some custom components have used a patch to register an on_change callback. Recently, we have done some refactoring that broke this workaround. This PR is a suggestion to extend our official API to make the patch redundant.
Note that we only want to pass the on_change_callback and not the args and kwargs. The register_widget function today uses args and kwargs as keywords to pass to the on_change callback. Besides the unfortunate naming - these are special keywords meant for functions themselves and not for pass-through arguments - we are thinking about deprecating them entirely, since you can wrap the callback easily to pass the arguments.
The way you can use it then looks like the following:
from functools import partial
import streamlit.components.v1 as components
_custom_component = components.declare_component("example", path=build_dir)
callback = partial(lambda x: f"On Change called with {x}", "some-value-for-x")
value = _custom_component("some-arg", other_arg="some-other-arg", on_change=callback)
"
So I worked with Claude Sonnet 4 and modified the streamlit-elements python files in the "core" folder.
Uploaded is what I have changed: callback.py, render.py, and frame.py
To use the "on_change".
And I have also uploaded an example Streamlit test page to show it's functionality "test_callback_functionality".