You can check out with I did for the components of my explainerdashboard
library here: https://explainerdashboard.readthedocs.io/en/latest/components.html
It has a number of features that are specific to this library (dependencies, explainer objects, etc), but a stripped down version would be:
class DashComponent:
"""DashComponent is a bundle of a dash layout and callbacks.
An DashComponent can have DashComponent subcomponents, that
you register with register_components().
Each DashComponent adds a unique uuid name string to all elements, so
that there is never a name clash even with multiple ExplanerComponents of
the same type in a layout.
Important:
define your callbacks in _register_callbacks() and
DashComponent will register callbacks of subcomponents in addition
to _register_callbacks() when calling register_callbacks()
"""
def __init__(self, title=None, name=None):
self.title = title
self.name = name
if self.name is None:
self.name = shortuuid.ShortUUID().random(length=10)
self._components = []
def register_components(self, *components):
"""register subcomponents so that their callbacks will be registered
and dependencies can be tracked"""
if not hasattr(self, '_components'):
self._components = []
for comp in components:
if isinstance(comp, DashComponent):
self._components.append(comp)
elif hasattr(comp, '__iter__'):
for subcomp in comp:
if isinstance(subcomp, DashComponent):
self._components.append(subcomp)
else:
print(f"{subcomp.__name__} is not a DashComponent so not adding to self.components")
else:
print(f"{comp.__name__} is not a DashComponent so not adding to self.components")
def layout(self):
"""layout to be defined by the particular DashComponent instance.
All element id's should append +self.name to make sure they are unique."""
return None
def _register_callbacks(self, app):
"""register callbacks specific to this DashComponent"""
pass
def register_callbacks(self, app):
"""First register callbacks of all subcomponents, then call
_register_callbacks(app)
"""
if not hasattr(self, '_components'):
self._components = []
for comp in self._components:
comp.register_callbacks(app)
self._register_callbacks(app)
(note that you need shortuuid
package to make sure all id’s are unique)
You could then write a custom component as:
class CustomComponent(DashComponent):
def __init__(self, starting_text="hello world!"):
super().__init__(title="Custom Component")
self.starting_text = starting_text
def layout(self):
return html.Div([
html.Div(id='output-div-'+self.name, children=self.starting_text),
html.Button("Add !", id='button-'+self.name),
])
def _register_callbacks(self, app):
@app.callback(
Output('output-div-'+self.name, 'children'),
Input('button-'+self.name, 'n_clicks'),
State('output-div-'+self.name, 'children')
)
def update_div(n_clicks, old_text):
return old_text + '!'
And combine multiple components into a single layout:
class CustomDashboard(DashComponent):
def __init__(self, starting_text1='Hello world', starting_text2='Bye World'):
super().__init__(title="! Adder")
self.custom1 = CustomComponent(starting_text1)
self.custom2 = CustomComponent(starting_text2)
self.register_components(self.custom1, self.custom2)
def layout(self):
return html.Div([
html.H1(self.title),
self.custom1.layout(),
self.custom2.layout()
])
And start your dashboard:
db = CustomDashboard()
app = dash.Dash()
app.title = db.title
app.layout = db.layout()
db.register_callbacks(app)
app.run_server()