Data_editor not changing cell the 1st time but only after the second time

The problem seems to be common to those trying to use the data_editor widget/tool – not getting the cell to change the 1st time but the second time it works. I have seen the responses to this but have not seen a solution. I have a session state defined with the and I have a on_change function/def setup although I am not sure what the on_change function should do other than change the session state to the data_editor object.

Could someone please walk thru this procedure for us in a complete working streamlit file?

1 Like

There are several examples in the docs.

1 Like

Yes there are some examples of how to use the data_editor, and that is helpful but can we show how to use a session state, with a data editor and a on_change within a data_editor. How these three items work together to retain edited values within a data_editor tool.
I have done this (setup a session state, setup a data_editor and a on_change function call) but still have problems. The 1st time time I change the data_editor the values do not change bit then the 2nd time I change the data_editor the value change and are retained.

1 Like

The modified data is the return value of data_editor(). I don’t know what you are doing in the on_change callback or why you think you need one.

1 Like

because I thought the on_change would execute before anything else - thinking that that was why the data_editor was not returning the changed value until the 2nd time. Because this too (the on_change) did not work I assume that an on_change is not needed to retain the changed values and that using a session state should work. I think you are saying that a on-change function call is not needed to retain the changes in data_editor, that setting the results from the data_editor to a session state is all that is needed. However this is still not working on the 1st change in the data_editor.

1 Like

The examples don’t have that problem, do they?

1 Like

I think I found the issue. I used a radio button to select different groups of columns. Then I used the column_order=list to show the group of columns. This did not work with the session state. I think the column_order=list in data_editor has a problem - it may reset the session state?

1 Like

Not anythong that I would call reset the session state, but I guess there are some ways in which it would cause trouble.

1 Like

here a simple example of the problem

import streamlit as st

@st.cache_data
def load_data():
initial_data = {‘item’: {‘Fd1’: ‘item 1’, ‘Fd2’: ‘item 1’, ‘Fd3’: ‘item 1’, ‘Fd4’: ‘item 1’, ‘Fd5’: ‘item 1’, ‘Fd6’: ‘item 1’, ‘Fd7’: ‘item 1’},
‘Name’: {‘Fd1’: ‘info 1’, ‘Fd2’: ‘info 1’, ‘Fd3’: ‘info 1’, ‘Fd4’: ‘info 1’, ‘Fd5’: ‘info 1’, ‘Fd6’: ‘info 1’, ‘Fd7’: ‘info 1’},
‘Data 1’: {‘Fd1’: 10, ‘Fd2’: 25, ‘Fd3’: 22, ‘Fd4’: 15, ‘Fd5’: 30, ‘Fd6’: 24, ‘Fd7’: 8},
‘Data 2’: {‘Fd1’: 2.5, ‘Fd2’: 3.0, ‘Fd3’: 4.0, ‘Fd4’: 4.2, ‘Fd5’: 3.0, ‘Fd6’: 1.8, ‘Fd7’: 0.8},
‘Data 3’: {‘Fd1’: .35, ‘Fd2’: 0.4, ‘Fd3’: 0.42, ‘Fd4’: 0.5, ‘Fd5’: 0.5, ‘Fd6’: 0.3, ‘Fd7’: 0.2}}
return initial_data

st.set_page_config(layout=“wide”,page_title=“Test Case”)

if ‘Fd_data’ not in st.session_state:
initial_data = load_data()
st.session_state[‘Fd_data’] = initial_data

tab1, tab2, tab3 = st.tabs([“Feed Data”, “Operating Data”, “Calibration Ylds”])

with tab1:
col1, col2, col3 = st.columns([4, 1, 4])
c1 = col1.container(border=True)
c2 = col2.container(border=True)
c3 = col3.container(border=True)

fd_sel = c1.radio('Select cases',['Select Item A', 'Select Item B', 'Select Item C'],
                  horizontal=True, label_visibility="collapsed")

if fd_sel == 'Select Item A':
    edit_list = ('Name','Data 1','Data 2')
if fd_sel == 'Select Item B':
    edit_list = ('Name','Data 2','Data 3')
if fd_sel == 'Select Item C':
    edit_list = ('Data 1','Data 2','Data 3')

# using the "column_order=edit_list" as show below does not work correctly
data_dic = c1.data_editor(st.session_state['Fd_data'],column_order=edit_list, hide_index=True )
# with out the column_order=edit_list this works well
data_dic = c1.data_editor(st.session_state['Fd_data'], hide_index=True )
1 Like

Other than making separate data_editor tables, is there a way to use the column_order= “some list” parameter in the data_editor to display different groups of columns in a single data_editor without the data going back to the initial or original data (i.e. a load data function). It seem like this might be a bug or I am missing something.

I cannot reproduce the problem with that code either. I don’t really understand the intent of the code, though, so I might be missing something.

I’m encountering the same issue: cell content updates are not always recorded when data is entered. Running the following simple code locally:

    row = st.session_state['row']
    new_row = st.data_editor(row,
                         hide_index=True,
                         disabled=["AI"],
                         use_container_width=True)
    print(new_row)
    st.session_state['row'] = new_row
    print(st.session_state['row'])

Exhibits the same behavior. After updating a cell and pressing Enter, the first update isn’t reflected, but the second update is. The print() statements used for debugging confirm that it is not related to st.session_state.

By the way, I’m also using AgGrid(df, ..), and it behaves the same way.
Adding observation:
it looks like when some type of object display refresh is running, the cell is not updated.

1 Like

For anyone still struggling with this problem, here are some more details. The problem (at least for streamlit version 1.42.2) may occur when any of the following arguments passed to st.data_editor change from the previous run: key , data , width , height , use_container_width , column_order , column_config and num_rows. You can run and play around with the following code snippet to see a demonstration of how any edit is ignored when the data or column_order change on every re-run.

import streamlit as st
with st.echo():
    import streamlit as st
    import pandas as pd
    import numpy as np


    def f(df, column_order=None):
        cols = st.columns(3)
        with cols[0]:
            st.write("data_editor:")
            edited_df = st.data_editor(data=df, column_order=column_order)
        with cols[1]:
            st.write("df:")
            st.dataframe(df)
        with cols[2]:
            st.write("edited_df:")
            st.dataframe(edited_df)

    st.header("When `df` is the same as in the previous run")
    st.markdown("Just as in the examples shown in the [documentation](https://docs.streamlit.io/develop/api-reference/data/st.data_editor), everything works as expected when we are always passing the **SAME** dataframe to `st.data_editor`!")
    f(df=pd.DataFrame(data={"col": [1, 2, 3]}))
    st.markdown("The problem may occur when any of the following arguments passed to `st.data_editor` change from the previous run: "
                "`key`, `data`, `width`, `height`, `use_container_width`, `column_order`, `column_config` and `num_rows`."
                " The following two subsections demonstrate two such cases.")

    st.header("When `df` is different from the previous run")
    st.markdown("This demonstrates that passing a **DIFFERENT** `df` to `st.data_editor()` on each re-run will cause any edit to be ignored and the returned `edited_df` will be identical to `df`. Test it by editing any of the rows in the data editor.")
    f(df=pd.DataFrame(data={"col": [1, 2, np.random.randint(100)]}))

    st.header("When `column_order` is different from the previous run")
    st.markdown("This demonstrates that passing a **DIFFERENT** `column_order` to `st.data_editor()` from the previous run will cause any edit to be ignored and the returned `edited_df` will be identical to `df`. Test it by editing any of the rows in the data editor.")
    if 'column_order' not in st.session_state:
        st.session_state['column_order'] = ["col2", "col3", "col1"]
    column_order = st.session_state['column_order']
    column_order.insert(0, column_order.pop())
    f(df=pd.DataFrame(data={
        "col1": [1, 2, 3],
        "col2": [4, 5, 6],
        "col3": [7, 8, 9]
        }),
     column_order=column_order)
    st.session_state['column_order'] = column_order

And here is a wrapper function of Streamlit’s original data_editor() function that you can use to solve this issue:

def dynamic_input_data_editor(data, key, **_kwargs):
    """
    Like streamlit's data_editor but which allows you to initialize the data editor with input arguments that can
    change between consecutive runs. Fixes the problem described here: https://discuss.streamlit.io/t/data-editor-not-changing-cell-the-1st-time-but-only-after-the-second-time/64894/13?u=ranyahalom
    :param data: The `data` argument you normally pass to `st.data_editor()`.
    :param key: The `key` argument you normally pass to `st.data_editor()`.
    :param _kwargs: All other named arguments you normally pass to `st.data_editor()`.
    :return: Same result returned by calling `st.data_editor()`
    """
    changed_key = f'{key}_khkhkkhkkhkhkihsdhsaskskhhfgiolwmxkahs'
    initial_data_key = f'{key}_khkhkkhkkhkhkihsdhsaskskhhfgiolwmxkahs__initial_data'

    def on_data_editor_changed():
        if 'on_change' in _kwargs:
            args = _kwargs['args'] if 'args' in _kwargs else ()
            kwargs = _kwargs['kwargs'] if 'kwargs' in _kwargs else  {}
            _kwargs['on_change'](*args, **kwargs)
        hy.session_state[changed_key] = True

    if changed_key in hy.session_state and hy.session_state[changed_key]:
        data = hy.session_state[initial_data_key]
        hy.session_state[changed_key] = False
    else:
        hy.session_state[initial_data_key] = data
    __kwargs = _kwargs.copy()
    __kwargs.update({'data': data, 'key': key, 'on_change': on_data_editor_changed})
    return hy.data_editor(**__kwargs)
1 Like

Hi RanYahalom,
Your code has been a great help to my project.
However, I am curious why it works correctly when using your code.

From what I understand, the execution sequence of your code is as follows:

  1. When the widget is first rendered, it stores the previous data inside the else branch.
  2. When the widget data changes, on_data_editor_changed updates session_state[changed_key] to True.
  3. In the if branch, the previously stored data from step 1 is retrieved and used to update __kwargs.

It seems like this approach ensures that the data passed to data_editor remains unchanged from the previous run.

I would love to understand how this approach guarantees correct behavior.

Is this related to this part of the Streamlit code?

Your insights would greatly help me understand how Streamlit.io works internally.

Once again, I truly appreciate your code! Thanks to you, I am now able to complete my project perfectly. :smiley: