[Feature Request] Target components with dcc.Loading
Is your feature request related to a problem? Please describe.
Generally, it can be frustrating that dcc.Loading will trigger a spinning animation when ANY of its nested components are in a callback. For instance, you might want a full-screen spinner to take over sometimes, but if you want a full-screen spinner takeover, that animation is going to run EVERY time that a callback in the app runs.... which is quite painful.
Describe the solution you'd like
New properties for dcc.Loading, e.g. property= and/or component_id= (or similar) which make it possible to target specific nested component properties and component IDs.
The code to handle this would likely go here: https://github.com/plotly/dash/blob/dev/components/dash-core-components/src/components/Loading.react.js#L54-L57
And look something like:
if (loading_state.prop != this.props.property) {
return this.props.children
}
Describe alternatives you've considered There isn't a good solution here. You can down-nest the loading spinner, but this might not be the right UX you're looking for in the app.
@ndrezn you can try the AntdSpin() in my components library fac, set parameter listenPropsMode='include', then only every target defined in includeProps will trigger Loading animation, here is a simple example:
fac.AntdSpace(
[
fac.AntdButton('not in includeProps', id='spin-include-demo-input1', type='primary'),
fac.AntdButton('in includeProps', id='spin-include-demo-input2', type='primary')
]
),
fac.AntdSpin(
fac.AntdSpace(
[
fac.AntdText('not in includeProps nClicks: 0', id='spin-include-demo-output1', strong=True),
fac.AntdText('in includeProps nClicks: 0', id='spin-include-demo-output2', strong=True)
],
direction='vertical'
),
listenPropsMode='include',
includeProps=['spin-include-demo-output2.children'],
text='running'
)
...
@app.callback(
Output('spin-include-demo-output1', 'children'),
Input('spin-include-demo-input1', 'nClicks'),
prevent_initial_call=True
)
def spin_include_callback_demo1(nClicks):
import time
time.sleep(1)
return f'not in includeProps nClicks: {nClicks}'
@app.callback(
Output('spin-include-demo-output2', 'children'),
Input('spin-include-demo-input2', 'nClicks'),
prevent_initial_call=True
)
def spin_include_callback_demo2(nClicks):
import time
time.sleep(1)
return f'in includeProps nClicks: {nClicks}'
What about adding a new property to the component, e.g. loading_properties. Then
const isLoading = loading_state && loading_state.is_loading;
changes into
let isLoading = false;
if (loading_state && loading_properties) {
const loadingProperty = loading_properties.find(([id, prop]) => {
return id === loading_state.component_name && prop === loading_state.prop_name;
});
isLoading = loading_state.is_loading && !!loadingProperty;
} else if (loading_state) {
isLoading = loading_state.is_loading;
}
where loading_properties is a list of tuples like [(<component_id>, <component_property>), (..)]. the idea is that the Loading spinner will only be shown if the component properties given in the list of tuples is loading. If the property is not specified, Loading should just do what it does now. I would like to test out above code, but I can't get the dev environment working for Dash.
Extra changes
To get above snippet working, update
type: spinnerType
to
type: spinnerType,
loading_properties
in the this.props call. Also add
/**
* List of tuples. Each tuple contains component id and the property name.
* Loading spinner will be shown only if the specified component's property is loading.
* If empty, the spinner will be shown if any property of any component is loading.
*/
loading_properties: PropTypes.arrayOf(PropTypes.array),
to Loading.propTypes
closed by #2760