Include multiple inputs in a callback

Viewed 56

So, I'm trying to create this form using dash. I want to read all the inputs from this form and throw it into a dataframe. I was able to reference a single input using the callback but I'm unable to reference multiple inputs to be returned using the callback. I'm very new to this.

What am I doing wrong here?

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

app.config.external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

# build your Dash app
app.layout = html.Div(
[
    html.H2('Form'),
    html.B("Reciever's Name:"),
    html.Br(),
    dcc.Input(id='input-1-state', type='text', placeholder='Enter Name', style={'color':'blue','font-family':'verdana'}),
    html.Br(),
    html.Br(),
    html.B("Reciever's Email:"),
    html.Br(),
    dcc.Input(id='input-2-state', type='email', placeholder='Enter email', style={'color':'blue','font-family':'verdana'}),
    html.Br(),
    html.Br(),
    html.B("Sender's Name:"),
    html.Br(),
    dcc.Input(id='input-3-state', type='text', placeholder='Enter name', style={'color':'red','font-family':'verdana'}),
    html.Br(),
    html.Br(),
    html.B("Sender's Email:"),
    html.Br(),
    dcc.Input(id='input-4-state', type='email', placeholder='Enter email', style={'color':'red','font-family':'verdana'}),
    html.Br(),
    html.Br(),
    html.B("Date:"),
    html.Br(),
    dcc.Input(id='input-5-state', type='date', placeholder='', style={'color':'red','font-family':'verdana'}),
    html.Br(),
    html.Br(),
    html.B("Location:"),
    dcc.Dropdown(['NYC', 'MTL', 'SF'], searchable=True, id='dropdown'),
    html.Br(),
    html.Br(),
    html.B("Select list of values she/he/they demonstrated"),
    html.Br(),
    html.Br(),
    dcc.Checklist(options=[
       {'label': 'New York City', 'value': 'NYC'},
       {'label': 'Montreal', 'value': 'MTL'},
       {'label': 'San Francisco', 'value': 'SF'}]),
    html.Br(),
    html.B("Tell us a little about xyz:"),
    html.Br(),
    dcc.Input(id='input-6-state', type='text', placeholder='Built a very useful dashboard for the sales team', style={'color':'blue','font-family':'verdana','width':'100%', 'height':'flex'}),
    html.Br(),
    html.Br(),
    
    
    html.Button(id='submit-val', n_clicks=0, children='Submit'),
    html.Br(),
    html.Br(),
    html.Div(id='output-container-button',children='Enter a value and press submit')
    
])

@app.callback(
    dash.dependencies.Output('output-container-button','children'),
    [dash.dependencies.Input('input-1-state','value'),
    dash.dependencies.Input('input-2-state','value')]
)

def update_output(value1,value2):
    return "Reciever's Name: {} and Reciveers email: {}".format(value,value)
1 Answers

The first error I got when I ran your code was related to dcc.Input because it does not accept the argument type='date' so instead you can pass type='text' and convert it to a datetime object later as necessary.

The following line should work:

dcc.Input(id='input-5-state', type='text', placeholder='', style={'color':'red','font-family':'verdana'}),

You should be able to store multiple inputs in a DataFrame – I am not sure how you want to use the DataFrame, but I created a input_data that is initialized to a dictionary with key-value pairs where the keys are the column names and the values are lists that can be appended to. Something like the following:

input_data = {
    "receiver_name": [],
    "receiver_email": [],
    "sender_name": [],
    "sender_email": [],
    "date": [],
    "info": [],
}

Then I modified your callback function update_output to append the input data to the values of this dictionary, and created a DataFrame inside the function using df = pd.DataFrame(input_data). I'll leave it up to you to decide how you want to use this DataFrame, but I tested it and it works.

Also one really important thing is that all of the dcc.Input components that you're passing to your callback should probably be State items, and the Submit button should be the only input: this means that the input_data will only be updated whenever you press the submit button (if you instead make all of the text boxes inputs along with the button, then the input_data will update every single time to change any of the text boxes, even if you don't press the button).

import dash
import pandas as pd
from dash import dcc, html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate

app = dash.Dash(__name__)
app.config.external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

# build your Dash app
app.layout = html.Div(
    [
        html.H2("Form"),
        html.B("Reciever's Name:"),
        html.Br(),
        dcc.Input(
            id="input-1-state",
            type="text",
            placeholder="Enter Name",
            style={"color": "blue", "font-family": "verdana"},
        ),
        html.Br(),
        html.Br(),
        html.B("Reciever's Email:"),
        html.Br(),
        dcc.Input(
            id="input-2-state",
            type="email",
            placeholder="Enter email",
            style={"color": "blue", "font-family": "verdana"},
        ),
        html.Br(),
        html.Br(),
        html.B("Sender's Name:"),
        html.Br(),
        dcc.Input(
            id="input-3-state",
            type="text",
            placeholder="Enter name",
            style={"color": "red", "font-family": "verdana"},
        ),
        html.Br(),
        html.Br(),
        html.B("Sender's Email:"),
        html.Br(),
        dcc.Input(
            id="input-4-state",
            type="email",
            placeholder="Enter email",
            style={"color": "red", "font-family": "verdana"},
        ),
        html.Br(),
        html.Br(),
        html.B("Date:"),
        html.Br(),
        dcc.Input(
            id="input-5-state",
            type="text",
            placeholder="",
            style={"color": "red", "font-family": "verdana"},
        ),
        html.Br(),
        html.Br(),
        html.B("Location:"),
        dcc.Dropdown(["NYC", "MTL", "SF"], searchable=True, id="dropdown"),
        html.Br(),
        html.Br(),
        html.B("Select list of values she/he/they demonstrated"),
        html.Br(),
        html.Br(),
        dcc.Checklist(
            options=[
                {"label": "New York City", "value": "NYC"},
                {"label": "Montreal", "value": "MTL"},
                {"label": "San Francisco", "value": "SF"},
            ]
        ),
        html.Br(),
        html.B("Tell us a little about xyz:"),
        html.Br(),
        dcc.Input(
            id="input-6-state",
            type="text",
            placeholder="Built a very useful dashboard for the sales team",
            style={"color": "blue", "font-family": "verdana", "width": "100%", "height": "flex"},
        ),
        html.Br(),
        html.Br(),
        html.Button(id="submit-val", n_clicks=0, children="Submit"),
        html.Br(),
        html.Br(),
        html.Div(id="output-container-button", children="Enter a value and press submit"),
    ]
)

input_data = {
    "receiver_name": [],
    "receiver_email": [],
    "sender_name": [],
    "sender_email": [],
    "date": [],
    "info": [],
}


@app.callback(
    Output("output-container-button", "children"),
    Input("submit-val", "n_clicks"),
    [
        State("input-1-state", "value"),
        State("input-2-state", "value"),
        State("input-3-state", "value"),
        State("input-4-state", "value"),
        State("input-5-state", "value"),
        State("input-6-state", "value"),
    ],
    prevent_initial_call=True,
)
def update_output(
    n_clicks,
    value1,
    value2,
    value3,
    value4,
    value5,
    value6,
    input_data=input_data,
):
    input_data["receiver_name"].append(value1)
    input_data["receiver_email"].append(value2)
    input_data["sender_name"].append(value3)
    input_data["sender_email"].append(value4)
    input_data["date"].append(value5)
    input_data["info"].append(value6)
    df = pd.DataFrame(input_data)
    # print(df)
    return "Receiver's Name: {} and Receiver's email: {}".format(value1, value2)


app.run_server(debug=True, host="0.0.0.0", port=8080)

Here are some test inputs:

enter image description here

Here is a print out of the input_df that your function updates after I click the submit button:

  receiver_name receiver_email sender_name   sender_email        date                      info
0           foo  foo@gmail.com         bar  bar@gmail.com  2022-08-28  Here is some sample text

And if I press the submit button again, the input_df is updated accordingly:

  receiver_name receiver_email sender_name   sender_email        date                      info
0           foo  foo@gmail.com         bar  bar@gmail.com  2022-08-28  Here is some sample text
1           foo  foo@gmail.com         bar  bar@gmail.com  2022-08-28  Here is some sample text

Related