From layout to cardbody

Hi everyone !

I coding a nutrition app for fun to track my diet ! I will present you my situation quickly and ask you a question (bold text).

I have a layout here :

recipe_layout = dbc.Container([
    html.Br(),
    dbc.Row([
        html.Br(),
        recipe_dropdown_layout,
        recipe_message_layout,
        html.Div(id='sliders-div'),
        # Store component to keep track of maximum slider index
        dcc.Store(id='slider-index-counter', data={'count': 0}),
        display_dropdown_layout,
    ]),
    dbc.Row([
            display_layout
        ]),
        menu_recipe_layout,
    dcc.Store(id='dropdown-options-store'),
    html.Div(id='recipe-page-user'),
    html.Div(id='profile-recipe-div'),
])

So to sum it up, you have a dropdowns menu where you can pick up your ingredient with the add button. Then it displays a slider with your ingredients as a label. So you can choose several ingredients and quantities. Finally you have graph menu selection (vitamin, macro nutriment, lipid, …) and the graphs which updates dynamically with the sliders !

My goal is to turn this all layout into a cardbody ! For my new layout I will just have a add button which will create new card and the cardbody will be my previous layout.
As I can have multiple dish I would like to have multiple layout and the new layout graph will count the sum of the dish.

So I guess the first step is :

recipe_layout = html.Div([
    html.Button('Add', id='add-button'),
    html.Div([], id='card-container')
])

@app.callback(
    Output('card-container', 'children'),
    [Input('add-button', 'n_clicks')],
    [State('card-container', 'children')]
)
def add_new_card(n_clicks, children):

So now there is the tricky part for me because, if I just copy paste I have the feeling it will be wrong. For example my slider are indexed by the add button. So If I have several cardbody with the add button, the slider will have the same index and then It will lead to major dysfunctionalities. So I need to specify my add button (somehow that’s why I’m here)

    dbc.Button('A', id='add-food-button', color="primary", n_clicks=0, className='btn btn-primary'),

And then the same work I guess for the callback of the sliders and the graph

@app.callback(
    [Output('sliders-div', 'children'),
     Output('slider-index-counter', 'data')],
    [Input('add-food-button', 'n_clicks'), 
     Input('save-dish-button', 'n_clicks'), 
     Input('load-dish-button', 'n_clicks'),
     Input({'type': 'remove-button', 'index': dash.dependencies.ALL}, 'n_clicks')],
    [State('dish-name-load', 'value'),
     State('food-dropdown', 'value'), 
     State('sliders-div', 'children'),
     State('profile-store', 'data'),
     State('slider-index-counter', 'data')]
)
def update_sliders(add_click, dish_click,load_click, remove_clicks, 
                   dish_name, value, children, data, counter_data):
@app.callback(
    Output('protein-graph', 'figure'),
    [Input({'type': 'dynamic-slider', 'index': dash.dependencies.ALL, 'food': dash.dependencies.ALL}, 'value')],
    [State({'type': 'dynamic-slider', 'index': dash.dependencies.ALL, 'food': dash.dependencies.ALL}, 'id')]
)
def update_macro_graph(values, ids):

My question is : Can you give me tips for the transition (how to change my code). I have to say I spent hour on it and It’s summer I wish I can wrap it up for now peacefully.

Thank you for your help

Hi @Plotlyoko I would recommend using pattern matching callbacks.

Take a look at this example:

1 Like

Hi @AIMPED

I’ve been implemented your suggestion, however I’m stuck now. I have an add button to add card. In the cardbody I have an add button to add slider. And according to the sliders values my graph update dynamically. The rest is my own way I can change it, if it leads to working app. So I have indexed my slider by card_index and slider_index :

dcc.Slider(
                    id={'type': 'dynamic-slider', 'card_index': card_index, 'slider_index': slider_index, 'food': value},
                    min=0,
                    max=500,
                    value=0,
                    className='my-4'
                ),

And the next step is to collect the value of all the slider of the card i to update the graph of the card i .
This is my guess :

@app.callback(
    Output({'type': 'slider-values-display', 'index': MATCH}, 'children'),
    [Input({'type': 'dynamic-slider', 'index': MATCH, 'food': ALL, 'slider_index': ALL}, 'value')]
)
def display_slider_values(*slider_values):

However it leads to issues which I didn’t know before. Do you have any idea how to code this function with pattern match ?

Hi @Plotlyoko to be honest I don’t understand what you are trying to do. I have never seen such a syntax and I don’t think this is going to work.

Could you break donw your preblem into a example which reproduces you problem so that we could try it on our computers?

Chat gpt provide me a solution, I still don’t really know why it solves it.

You told me that you don’t get the hang of my app : To sum up quickly the difficulty was to index the slider then they can match one cardbody (I can add several).
And the final step is to catch the label, value of the sliders in one specific card to update the graph. And the solution I have tried :

@app.callback(
    Output({'type': 'slider-values-display', 'index': MATCH}, 'children'),
    [Input({'type': 'dynamic-slider', 'card_index': MATCH, 'food': ALL, 'slider_index': ALL}, 'value')],
    [State({'type': 'dynamic-slider', 'card_index': MATCH, 'food': ALL, 'slider_index': ALL}, 'food')]
)
def display_slider_values(slider_values, slider_labels):

Thank you for you help.

Does this mean, you do not need help anymore?

If you want

2 Likes

My goal is to code a sustainable app so I prefer to understand what I do that’s why I’m here. If I took time to write all those posts with the screenshot is because I’m looking for advice from the forum.

So I would listen any solution which is proposed because I want to progress