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)

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!
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 valueattribute, a valueattribute 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.
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.
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
Nonebecause 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 toNonein Python) eg> document.getElementById('id_of_input_with_no_value').getAttribute('value') < nullSo when a user modifies the contents of an input element with no
valueattribute, avalueattribute 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
valueof the empty string for Inputs, or otherwise explicitly handle the possibility ofNonein 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.
Most helpful comment
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
Nonebecause 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 toNonein Python) egSo when a user modifies the contents of an input element with no
valueattribute, avalueattribute 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
valueof the empty string for Inputs, or otherwise explicitly handle the possibility ofNonein addition to the empty string.