# Dynamically create callbacks on buttons inside existing callback

**URL:** <https://community.plotly.com/t/dynamically-create-callbacks-on-buttons-inside-existing-callback/37660>\
**Category:** Dash Python\
**Created:** [April 12, 2020, 4:34pm UTC](https://community.plotly.com/t/dynamically-create-callbacks-on-buttons-inside-existing-callback/37660 "2020-04-12T16:34:52Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![didouu47](https://avatars.discourse-cdn.com/v4/letter/d/7cd45c/32.png) [@didouu47](https://community.plotly.com/u/didouu47)\
**Post date:** [April 12, 2020, 4:34pm UTC](https://community.plotly.com/t/dynamically-create-callbacks-on-buttons-inside-existing-callback/37660/1 "2020-04-12T16:34:52Z")

</div>

Hello the community,

I am trying to build an e-commerce website using dash that can be found [https://carrefourcontactfontenilles.herokuapp.com/](https://carrefourcontactfontenilles.herokuapp.com/)

The app contains 3 tabs :

- “Catalogue de produits” which is the products catalogue
- “Panier” which will be the basket of the customer
- “Validation commande” which will be to validate the order

Actually I am done with the fist tab “Catalogue de produits”  
If you click on it, and use the drop downs present in the navbar you will see the correspond products.

My issue now is that I want to assign a callback for every button “Ajouter” (add) present on each product card, which will update as output the basket list of the customer.

After have read a lot on this forum, I have found the below method to assign a callback having input all the buttons (each button id is the article# present in the column data[‘Article’] :

```auto
List_button = data['Article'].tolist()
input_list=[Input('{}'.format(tool),'n_clicks_timestamp') for tool in List_button]
		
@app.callback(Output('session_3','children'),
				  input_list)

def update_panier2(b_list=[('b{}'.format(tool)) for tool in range(1,len(List_button))]):
    try:
        INDEX=b_list.index(max(filter(lambda x: x is not None,b_list)))
        return html.Div([List_button[INDEX]])

```

I use an output in a html.div for test purpose to return the reference# of the corresponding product where the button has been clicked, with ‘data’ being my database with more than 6,000 products. I used ‘n\_clicks\_timestamp’ to identify the latest button clicked.

However doing 6,000 callback in one time make the app impossible to use.

I am trying now to create the button callbacks inside a function which is inside the callback which refresh the product catalogue based on the drop down selection:

```auto
#update product list
@app.callback(
    [Output('product_list', 'children')
	],
    [Input('cat1', 'value')])

def update_layout(department):
    
    fi1=data[data['Cat2'] == str(department)]
    list_nom=fi1['Name'].tolist()
    list_EAN=fi1['EAN'].tolist()
    list_pack=fi1['Pack'].tolist()
    list_prix=fi1['Prix'].tolist()
    list_ref=fi1['Article'].tolist()
    #input_list=[Input('{}'.format(tool),'n_clicks_timestamp') for tool in list_ref]
    
    
    rws= len(fi1) 
    title="test"
    gridLayout = []
    card=[]
    
    for r in range(0, rws):
             
        try:
			   
            title=list_nom[r]
            EAN=list_EAN[r]
            Pack = list_pack[r]
            Prix = list_prix[r]
            Ref = list_ref[r]
				
                
            image_filename = 'Picture/' + str(EAN) + '.png'
            encoded_image = base64.b64encode(open(image_filename, 'rb').read())
            
            card2=dbc.Col([
            dbc.Card([		
            dbc.CardImg(src='data:image/png;base64,{}'.format(encoded_image.decode()), top=True),
            dbc.CardBody([
            html.H5(title, className="card-title"),
            html.P(Pack,className="card-text",),
            ],
            style={"height": "10rem"}),		
						dbc.Col([dcc.Dropdown(id='demo-dropdown',style={'font-size': "130%"},value=1,options=[{'label': '1', 'value': 1},
														                  {'label': '2', 'value': 2},
																		  {'label': '3', 'value': 3},
																		  {'label': '4', 'value': 4},
																		  {'label': '5', 'value': 5},
																		  {'label': '6', 'value': 6},
																		  {'label': '7', 'value': 7},
																		  {'label': '8', 'value': 8},
																		  {'label': '9', 'value': 9},
																		  {'label': '10', 'value': 10},
														                   ],placeholder="Quantité")]),
						dbc.Col([dbc.Button('Ajouter',id=str(Ref),color="primary",n_clicks=0)]),
			    dbc.CardFooter(html.H4('Reference : ' + str(Ref))),
				dbc.CardFooter(html.H4(str(Prix) + ' EUR'))
				])],md=3,xs=6,sm=6,lg=3,xl=3)
                
            if r!=rws+1:
                card.append(card2)
            elif r == 1:
                card.append(card2)
            else:
                card.append(",",card2)
        
        except:

            title=list_nom[r]
            EAN=list_EAN[r]
            Pack = list_pack[r]
            Prix = list_prix[r]
            Ref = list_ref[r]
				
                
            image_filename = 'Picture/non_dispo2.png'
            encoded_image = base64.b64encode(open(image_filename, 'rb').read())

            card2=dbc.Col([
            dbc.Card([		
            dbc.CardImg(src='data:image/png;base64,{}'.format(encoded_image.decode()), top=True),
            dbc.CardBody([
            html.H5(title, className="card-title"),
            html.P(Pack,className="card-text",),
            ],
            style={"height": "10rem"}),		
						dbc.Col([dcc.Dropdown(id='demo-dropdown',value=1,style={'font-size': "130%"},options=[{'label': '1', 'value': 1},
														                  {'label': '2', 'value': 2},
																		  {'label': '3', 'value': 3},
																		  {'label': '4', 'value': 4},
																		  {'label': '5', 'value': 5},
																		  {'label': '6', 'value': 6},
																		  {'label': '7', 'value': 7},
																		  {'label': '8', 'value': 8},
																		  {'label': '9', 'value': 9},
																		  {'label': '10', 'value': 10},
														                   ],placeholder="Quantité")]),
						dbc.Col([dbc.Button('Ajouter',id=Ref,color="primary")]),
			    dbc.CardFooter(html.H4('Reference : ' + str(Ref))),
				dbc.CardFooter(html.H4(str(Prix) + ' EUR'))
				])],md=3,xs=6,sm=6,lg=3,xl=3)
                
            if r!=rws+1:
                card.append(card2)
            elif r == 1:
                card.append(card2)
            else:
                card.append(",",card2)
			
			

    fi2=data[data['Cat2'] == str(department)]
    List_button = fi2['Article'].tolist()
    input_list=[Input('{}'.format(tool),'n_clicks_timestamp') for tool in List_button]
		
    @app.callback(Output('session_3','children'),
				  input_list)
		
    def update_panier2(b_list=[('b{}'.format(tool)) for tool in range(1,len(List_button))]):
        try:
           INDEX=b_list.index(max(filter(lambda x: x is not None,b_list)))
           return html.Div([List_button[INDEX]])
        except:
           return html.Div('not working-:(')	
    
	
	
    c=html.Div(card,className='row') 
    gridLayout.append(c)
    
    
    return gridLayout

```

I have an error on the App  
dash.exceptions.DuplicateCallbackOutput:  
You have already assigned a callback to the output  
with ID “session\_3” and property “children”. An output can only have  
a single callback function. Try combining your inputs and  
callback functions together into one function.

It is my first time using dash and I am blocked. If you have some suggestion on the way I can achieve my objective I thank you in advance

Thank you  
David

---

<div class="post-metadata">

**Author:** ![chriddyp](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/chriddyp/32/16855_2.png) [@chriddyp](https://community.plotly.com/u/chriddyp)\
**Post date:** [April 12, 2020, 5:17pm UTC](https://community.plotly.com/t/dynamically-create-callbacks-on-buttons-inside-existing-callback/37660/2 "2020-04-12T17:17:21Z")

</div>

> [@didouu47](#):
>
> My issue now is that I want to assign a callback for every button “Ajouter” (add) present on each product card, which will update as output the basket list of the customer.

Check out the new pattern-matching callbacks feature released on Friday in Dash 1.11.0. It was built exactly for this use case.

- Announcement: [📣 Dash v1.11.0 Release - Introducing Pattern-Matching Callbacks - #5 by meg](https://community.plotly.com/t/dash-v1-11-0-release-introducing-pattern-matching-callbacks/37592/5)
- Documentation: [Pattern-Matching Callbacks | Dash for Python Documentation | Plotly](https://dash.plotly.com/pattern-matching-callbacks)

---

<div class="post-metadata">

**Author:** ![didouu47](https://avatars.discourse-cdn.com/v4/letter/d/7cd45c/32.png) [@didouu47](https://community.plotly.com/u/didouu47)\
**Post date:** [April 12, 2020, 5:34pm UTC](https://community.plotly.com/t/dynamically-create-callbacks-on-buttons-inside-existing-callback/37660/3 "2020-04-12T17:34:41Z")

</div>

🙂 thanks a lot chriddyp I have tested it and it works !!

---

<div class="post-metadata">

**Author:** ![didouu47](https://avatars.discourse-cdn.com/v4/letter/d/7cd45c/32.png) [@didouu47](https://community.plotly.com/u/didouu47)\
**Post date:** [April 13, 2020, 5:37pm UTC](https://community.plotly.com/t/dynamically-create-callbacks-on-buttons-inside-existing-callback/37660/4 "2020-04-13T17:37:16Z")

</div>

Hello @chriddyp 🙂 ,

I was wondering if you could please help me one more time.  
I used the pattern-matching callbacks features for my buttons with state being a dropdown value.

I am using now the JSON dash.callback\_context.states\_list converted to DataFrame where I filter only the button id with state value \> 0 (button and corresponding dropdown having the same ID)

I have tried the below code on my Notebook, which work :

```auto
# example of JSON dash.callback_context.states_list, from the callback output
json_file=[
  [
    {
      "id": {
        "index": "2148467",
        "type": "drop"
      },
      "property": "value",
      "value": 4
    },
    {
      "id": {
        "index": "1090464",
        "type": "drop"
      },
      "property": "value",
      "value": 0
    }
  ]
]

# convert json file into dataframe
fi1 = pd.DataFrame.from_dict(json_file[0], orient='columns')

# get number of clicks>0
fi1=fi1[fi1['value']>0]

#Retrieve button id 
fi1['Reference'] = fi1['id'].apply(lambda x: x.get('index'))
fi1=fi1[['Reference','value']]
fi1

```

On my notebook I get the result needed :  
 ![plot](https://us1.discourse-cdn.com/flex024/uploads/plot/original/2X/2/2b18d11ed0e436e58b44e979a38b315d2d7cc213.jpeg)

However when I paste the same code in my dash app :

```auto
@app.callback(

    Output('session_1', 'children'),

    [Input({'type': 'add_button', 'index': ALL}, 'n_clicks')],

    [State({'type': 'drop', 'index': ALL}, 'value')],

    )

def display_output(n,value):

    ctx = dash.callback_context

    ctx_msg = json.dumps(ctx.states_list,

     indent=2)

    fi1 = pd.DataFrame.from_dict(ctx_msg[0], orient='columns')

    fi1=fi1[fi1['value']>0]

    fi1['Reference'] = fi1['id'].apply(lambda x: x.get('index'))

    fi1=fi1[['Reference','value']]

    final=fi1.to_json(orient='index')

    

    return (html.Pre(final))

```

I received the below error\_message :

![error](https://us1.discourse-cdn.com/flex024/uploads/plot/original/2X/4/480f1ea8d08de503d46df917cfd71941da646e35.jpeg)

Could you please give me a clue where I am wrong

Thank you

David

---

<div class="post-metadata">

**Author:** ![didouu47](https://avatars.discourse-cdn.com/v4/letter/d/7cd45c/32.png) [@didouu47](https://community.plotly.com/u/didouu47)\
**Post date:** [April 13, 2020, 8:05pm UTC](https://community.plotly.com/t/dynamically-create-callbacks-on-buttons-inside-existing-callback/37660/5 "2020-04-13T20:05:19Z")

</div>

Ok I have replaced

ctx\_msg = json.dumps(ctx.states\_list,indent=2)

by

ctx\_msg = ctx.states\_list

it works now
