How can I update the children of a component whenever an element is dragged and dropped onto it? I’m running way before I’m crawling with JavaScript, so I’m a little unsure how to accomplish this.
I think what I need to do to start is have a clientside_callback
that will appendChild
the element being dragged/dropped to the parent element I’m dropping on. Then I need to trigger a callback
whenever the children in the parent element changes so that the I can use a python function to update the table. Am I on the right track here? Can anyone point to or provide an example of what I’m trying to accomplish?
Here’s my code if you want to try some stuff out:
app.py
from dash import Dash, dcc, html, dash_table, callback, clientside_callback
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State, ClientsideFunction
import pandas as pd
df = pd.DataFrame({
'Parameter 1': [i for i in range(10)],
'Parameter 2': [i + 2 for i in range(10)],
'Parameter 3': [i * 2 for i in range(10)],
'Parameter 4': [i / 2 for i in range(10)],
'Parameter 5': [i ** 2 for i in range(10)]
})
list_of_parameters = [dbc.Card(dbc.CardHeader(i, className="card-container")) for i in df.columns]
app = Dash(
__name__,
external_scripts=["https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.2/dragula.min.js"],
external_stylesheets=[dbc.themes.BOOTSTRAP],
prevent_initial_callbacks=True
)
app.layout = html.Div(
id="parameter-container",
children=[
dbc.Card([
dbc.CardHeader("Available Parameters"),
dbc.CardBody([
dbc.Button(
"Show",
id="parameter-collapse-button"
),
dbc.Collapse(
html.Div(
id="parameter-collapse-div",
children=list_of_parameters[2:],
),
id="parameter-collapse",
is_open=False,
)
])
]),
dbc.Card([
dbc.CardHeader("Selected Parameters"),
dbc.CardBody(
id="selected-parameter-div",
children=list_of_parameters[:2],
)
]),
dash_table.DataTable(
id="data-table",
columns=[],
data=df.to_dict('records'),
filter_action="native",
sort_action="native",
page_action="native",
page_current=0,
page_size=10,
)
]
)
@callback(
Output("parameter-collapse", "is_open"),
[Input("parameter-collapse-button", "n_clicks")],
[State("parameter-collapse", "is_open")]
)
def toggle_collapse(nclicks, is_open):
if nclicks:
return not is_open
return is_open
clientside_callback(
ClientsideFunction(namespace="clientside", function_name="make_draggable"),
Output("parameter-container", "data-drag"),
[
Input("parameter-collapse-div", "id"),
Input("selected-parameter-div", "id")
]
)
@callback(
Output("data-table", "columns"),
[Input("selected-parameter-div", "children")]
)
def update_columns(parameters):
# parameters should always include everything in selected-parameter-div
# and not just want it is initalized with.
if parameters:
cols = []
for parameter in parameters:
cols.append(parameter['props']['children']['props']['children'])
return [{'name': c, 'id': c, 'deletable': False} for c in cols]
if __name__ == "__main__":
app.run_server(debug=True)
assets/scripts.js
if (!window.dash_clientside) {
window.dash_clientside = {};
}
window.dash_clientside.clientside = {
make_draggable: function(rightID, leftID) {
setTimeout(function() {
var rightEl = document.getElementById(rightID)
var leftEl = document.getElementById(leftID)
window.console.log(rightEl)
window.console.log(leftEl)
dragula([rightEl, leftEl])
}, 1)
return window.dash_clientside.no_update
}
}
assets/dragula.css
.gu-mirror {
position: fixed !important;
margin: 0 !important;
z-index: 9999 !important;
opacity: 0.8;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)";
filter: alpha(opacity=80);
}
.gu-hide {
display: none !important;
}
.gu-unselectable {
-webkit-user-select: none !important;
-moz-user-select: none !important;
-ms-user-select: none !important;
user-select: none !important;
}
.gu-transit {
opacity: 0.2;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)";
filter: alpha(opacity=20);
}
assets/styles.css
#parameter-container {
margin: 1%;
}
.card-container {
display: inline-block;
}