Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dcc.Input value on page load vs. after deleting contents #162

Closed
jwhendy opened this issue Nov 27, 2017 · 3 comments
Closed

dcc.Input value on page load vs. after deleting contents #162

jwhendy opened this issue Nov 27, 2017 · 3 comments

Comments

@jwhendy
Copy link

jwhendy commented Nov 27, 2017

I just stumbled on unexpected behavior and I'm not sure if it should be considered a bug, or if this is desired. In my code, I let the user fill in some text boxes, then click a button which triggers the callback. I was forming a list of the non-blank dcc.Input contents using a check for not None. Here's an example:

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, Event, State

app = dash.Dash(__name__)
server = app.server
app.css.append_css({'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'})

app.layout = html.Div([
    html.H2('dash input on load vs. deleting content'),
    html.Label('boxes 1, 2, 3'),
    dcc.Input(id='box_1', type='text', placeholder='type something'),
    dcc.Input(id='box_2', type='text', placeholder='type something'),
    dcc.Input(id='box_3', type='text', placeholder='type something'),
    html.Div(id='content')])

@app.callback(
    Output('content', 'children'),
    [Input('box_1', 'value'),
     Input('box_2', 'value'),
     Input('box_3', 'value')])
def contents(box_1, box_2, box_3):
    boxes = [box_1, box_2, box_3]

    return ['[{}]'.format(box) for box in boxes if box is not None]

    
if __name__ == '__main__':
    app.run_server(debug=True)

anim

In real life, I'm doing an API call; during some debugging I noticed that the call was failing. It turned out that after deleting a box and re-running, my not None check didn't work since the box now contained ''.

I've been converting my mind to this sentiment from @chriddyp :

One core concept with Dash is that the app is described entirely by its current state and not by the order of events.

In a sense, the input boxes have been "tampered with", so I get why it's happening, but visually, the "state" would seem to be the same as at page load. How would one differentiate a "truly empty" box vs. one that's been erased?

Now that I've caught the "gotcha," I could change my check to look for None or ''. Is that best practice? Or perhaps I should have been initializing to '' all along vs. relying on None? Otherwise, it seems dash will, by default, differentiate between "original state" and "returned to original state"?

Thanks for any suggestions!

@ned2
Copy link
Contributor

ned2 commented Nov 28, 2017

I think I've run into this gotcha before. While this seems to contradict Dash's stateless nature, I actually don't think it does. Dash is intended to be stateless on the server-side however it should very much be stateful on the client-side, as we expect the results of callbacks to change depending on the current contents of the user's DOM.

In this example, the Dash Inputs only are assigned None because you didn't give them an initial value. This is what would be expected, since on the client side, the value of non-set attribute is null (which corresponds to None in Python) eg

> document.getElementById('id_of_input_with_no_value').getAttribute('value')
< null

So when a user modifies the contents of an input element with no value attribute, a value attribute will be created and can then only have it's value set to '' as the user cannot remove the attribute without resorting to opening up the DOM inspector. So you don't have to think about this in terms of the order of events, you can just think of it as a particular state of the DOM that happens to be irreversible by the user.

I think this means that either you want to provide an initial value of the empty string for Inputs, or otherwise explicitly handle the possibility of None in addition to the empty string.

@jwhendy
Copy link
Author

jwhendy commented Nov 28, 2017

I can roll with that explanation of the behavior. Thanks @ned2 !

For others, the key takeaway for me was this:

Or perhaps I should have been initializing to '' all along vs. relying on None

That's what I will consider best practice now.

@jwhendy jwhendy closed this as completed Nov 28, 2017
byronz added a commit that referenced this issue Apr 23, 2019
Don't display "PreventUpdate" errors in the dev tools UI
@AndresJaramillo95
Copy link

I think I've run into this gotcha before. While this seems to contradict Dash's stateless nature, I actually don't think it does. Dash is intended to be stateless on the server-side however it should very much be stateful on the client-side, as we expect the results of callbacks to change depending on the current contents of the user's DOM.

In this example, the Dash Inputs only are assigned None because you didn't give them an initial value. This is what would be expected, since on the client side, the value of non-set attribute is null (which corresponds to None in Python) eg

> document.getElementById('id_of_input_with_no_value').getAttribute('value')
< null

So when a user modifies the contents of an input element with no value attribute, a value attribute will be created and can then only have it's value set to '' as the user cannot remove the attribute without resorting to opening up the DOM inspector. So you don't have to think about this in terms of the order of events, you can just think of it as a particular state of the DOM that happens to be irreversible by the user.

I think this means that either you want to provide an initial value of the empty string for Inputs, or otherwise explicitly handle the possibility of None in addition to the empty string.

Hey, what would be the best way to go about from implementing a PreventUpdate when that happens? For my case having a pre inputted value doesn't suit me sadly. Trying to solve this for my current Dashboard that gets all buggy when a user erases all inputs in the callback.

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

No branches or pull requests

3 participants