Path: blob/main/doc/tutorials/intermediate/reusable_components.md
2012 views
Reusable Components
In this guide, we will explore how to structure our components to make them easily reusable and avoid callback hell:
Write
Parameterized
andViewer
classes that encapsulate multiple components.
Writing Parameterized Classes
When creating larger Panel projects, we recommend using Parameterized
classes. This approach is useful for several reasons:
Organizing intricate sections of code and functionality
Crafting reusable components composed of multiple Panel objects
Incorporating validation and documentation
Facilitating seamless testing
A Parameterized class must inherit from param.Parameterized
and should declare one or more parameters. Here, we will start building a DataExplorer
by declaring two parameters:
data
: Accepts a DataFramepage_size
: Controls the page size
This explorer doesn't do anything yet, so let's learn how we can turn the UI-agnostic parameter declarations into a UI. For that purpose, we will learn about pn.Param
.
pn.Param
allows mapping parameter declarations to widgets that allow editing the parameter value. There is a default mapping from Parameter
type to the appropriate type, but as long as the input matches, this can be overridden.
Let's start with the simplest case:
Notice that each parameter was mapped to a widget appropriate for editing its value, i.e., the data
was mapped to a Tabulator
widget, and the page_size
was mapped to an IntInput
widget.
If you try playing with the page_size
widget, you will notice that it doesn't actually do anything.
So next, let's explicitly map the parameter to a widget using the Widget.from_param
method. This will also let us provide additional options, e.g., to provide start
and end
values for the slider and layout options for the table.
Exercise: Add Typehints
:::{tip} If you or your team are working in editors or IDEs like VS Code or PyCharm, or using static analysis tools like mypy, we recommend adding type hints to your reusable Parameterized
classes. :::
Please add typehints to the DataExplorer
.
:::{dropdown} Solution: Basic
:::
:::{dropdown} Solution: Extended
:::
:::{note} We hope and dream that Param 3.0 will function much like dataclasses
, enabling editors, IDEs, and static analysis tools like mypy to automatically infer parameter types and __init__
signatures. :::
Creating Reusable Viewer Components
The whole point of using classes is to encapsulate the logic in them, so let's do that. For that, we can use a slight extension of the Parameterized
class that makes the object behave as if it were a regular Panel object. The Viewer
class does exactly that; all you have to do is implement the __panel__
method:
Exercise: Extend the DataExplorer
Extend the DataExplorer
class by adding parameters to control the Tabulator theme
and toggling the show_index
option
:::{dropdown} Solution
:::
Allow References
We can make our components much more flexible if we allow their parameters to take references. We can do this by setting allow_refs=True
on the parameters.
References can be
Parameters
Widgets (
.value
)Bound functions (
pn.bind
or@pn.depends
)Reactive Expressions (
.rx
)Sync and async generators (
yield
)
Lets take a simple example where use a widget as an argument instead of a string.
If you want to learn more about references try using other types of references as input to the GoogleMapViewer
.
Recap
We have learned how to structure our components to make them easily reusable and avoid callback hell.
We should now be able to write reusable Parameterized
and Viewer
classes that encapsulate multiple components.