Introduction to dynamic elements¶
The previous section on static elements exposes how element libraries and elements are defined. A major limitation with static elements is that you cannot change their property values at runtime. In Taipy GUI, you can bind Python variables or expressions to properties so that the user interface is immediately updated should the application modify the value of a variable.
Custom elements that allow for variable binding are called dynamic elements
in Taipy GUI.
As we have seen in the previous section, every custom element must declare
the properties that it relies on. Doing so means that every property must indicate
the type it supports. This is handled by the PropertyType
class.
Every custom element that wants to use a property with a dynamic type
must be defined as a dynamic element.
Although a custom element might not have any dynamic property (that is, a property
with a type that is dynamic), it can be implemented as a dynamic element nevertheless
to leverage the expressivity of how these elements are implemented.
Dynamic elements use TypeScript and JavaScript code to dynamically generate HTML code to produce the pages that can be displayed in a browser. Taipy GUI relies on the React JavaScript library to simplify the development of graphical components.
Minimal knowledge
The following sections contain code samples written in TypeScript that leverage the React library. This manual is in no way a beginner's guide on either technology.
To create your custom dynamic elements, you need to know about React functional
components and hooks and how to create simple components.
You also need to know the basics about packaging React components using
webpack that bundles the JavaScript modules into
a JavaScript library that element libraries can load to implement the front-end
part of the custom elements.
Declaring a dynamic element¶
The declaration of a dynamic element looks very similar to the declaration of a static
element.
The fundamental change is that if the render_xhtml argument of the
Element
constructor is not set or is not a function
then the element is considered dynamic. That is, implemented using a React
component. You can specify the name of the component using the react_component
argument. If you don't, Taipy GUI will use a capitalized camel case transformation
of the element name as the target React component name.
Here is how a custom element library containing a dynamic element would be declared:
from taipy.gui.extension import ElementLibrary, Element, ElementProperty
class CustomLibrary(ElementLibrary):
def __init__(self) -> None:
self.elements = {
...
"<element1 name>": Element("<default property name>", {
"<property1 name>": ElementProperty(<property1 type>, ...),
...
},
# This is optional and can be omitted if the component name can be inferred
# from the element name
react_component="<element1 component name>"),
...
}
def get_name(self) -> str:
return "<library name>"
def get_elements(self) -> dict:
return self.elements
def get_scripts(self) -> list[str]:
return ["<javascript module pathname>"]
There are two significant differences to spot compared to a custom element library that has only static elements:
- A dynamic element is created using the react_component parameter, indicating that it is implemented using a React component. The value of this parameter must be the name of the React component, as exported by the JavaScript module where this component is defined;
- The method
get_scripts()
must be overloaded to return the path to the JavaScript bundle file that contains the definition for the React components of the dynamic elements. The code above assumes that the custom element library needs only one file.
Implementing a dynamic element¶
The front-end side of dynamic elements is implemented as standard React components.
We recommend defining each component in its own source file for a clear code
organization. In this manual, we will exclusively use TypeScript to enjoy better type
checking and make the code more readable. The
custom element library template repository
provides the skeleton of an
extension library project structure.
Here are the key things to know when creating a React component that implements the front-end for a custom visual element:
- The component name should be one used when the element is declared, using the
react_component argument of the
Element
constructor.
If this parameter is not used, Taipy GUI uses a camel case transformation of the element name. - All component source files are bundled in a JavaScript library. We are using webpack for this purpose.
- Components must be exported by the JavaScript library with the same name as the one
used in the
Element
constructor. - Property names must be valid Python identifiers.
- The camel case transformation of the property names, with a lowercase initial letter, are
used to name the keys of the props argument for the React component.
We recommend that we declare a TypeScript interface that provides better typing for this argument: each property of the interface should have the transformed name of the element property.
Note that you can overwrite the name of the property in the React component by using the js_name argument of theElementProperty
constructor. -
Dynamic properties use two keys in the functional component props argument:
- The (transformed) property name as described above;
- A similar camel case-transformed version of the property name as if it had been
prefixed by 'default_'.
This slot in the props is used to render the component before the Taipy GUI back-end has submitted any update (which is then handled by the other slot).
To illustrate this, let's assume we want to create a dynamic custom element called sizable_label. This element has two dynamic properties:
- label, the default property, stores a string that the element should display;
- size is a numeric property that stores how large the label should be rendered.
The element declaration would look like this in the definition of the dictionary that holds all the element declarations:
"sizable_label": Element("label", {
"label": ElementProperty(PropertyType.dynamic_string),
"size": ElementProperty(PropertyType.dynamic_number)
}
The default name for the React component would be "SizableLabel", and its properties
will be named after the element's property names.
Here is how the definition for the React component will look like (typically, this code
will be stored in a file called "SizableLabel.tsx", in the directory
"<project dir>/<package dir>/front-end/src"):
interface SizableLabelProps {
label?: string;
defaultLabel: string;
size?: number;
defaultSize: number;
}
const SizableLabel = (props: SizableLabelProps) => {
...
}
When the component is entirely defined, it must be exported by the JavaScript library.
This is done by adding the export directive in the file
"<project dir>/<package dir>/front-end/src/index.ts".
For our example, this would be the content of this file:
import SizableLabel from "./SizableLabel";
export { SizableLabel };
Building the front-end module¶
To build the JavaScript module that Taipy GUI can load on the user's browser, there are three steps to take:
- Convert the TypeScript code to JavaScript;
- Resolve the dependency in the Taipy GUI JavaScript module;
- Bundle all JavaScript code into the extension library JavaScript module.
All the steps are handled by the package.json
file located at the root of the front-end
NPM project (that is, in the <package dir>/front-end
directory in our examples).
The commands declared in this file heavily rely on webpack
to handle the build process: the "scripts" property of the package.json
content defines
the "build" entry as invoking "webpack".
The webpack.config.js
configuration file should sit next to package.json
. This
file contains all the configuration parameters that we will discuss in the rest of this
section.
Converting TypeScript¶
TypeScript is a language that is transformed into JavaScript before it can be packaged
in a JavaScript bundle. This process is known as 'transpilation'.
Here is how the project must be set up in order to use TypeScript and support the React
JSX syntax (used in .tsx
source files):
-
To transpile TypeScript, you need to install the NPM modules "typescript" and "ts-loader":
npm install --save-dev typescript ts-loader
Note that in the
package.json
provided with Taipy GUI, these two modules already appear in the "devDependencies" property. -
A file called
tsconfig.json
must exist next to the filewebpack.config.js
. This file contains the configuration to support JSX and convert TypeScript to vanilla JavaScript.
Here is an excerpt of thetsconfig.json
file we use in the dynamic library examples. We do not explain all the settings that appear in the file we provide and let the reader take a peek at the TSConfig documentation for a complete description of thetsconfig.json
file content:Note that you must make sure that the directory where TypeScript files are located (the "include" property) and the output directory (the "outDir" property) are set according to your project structure.{ "compilerOptions": { "target": "es5", "outDir": "./dist/", "module": "esnext", "moduleResolution": "node", "jsx": "react-jsx", "allowJs": true, }, "include": [ "src" ] }
-
webpack
must be configured so it can transpile TypeScript code.
The two essential settings in thewebpack.config.js
file are shown here:resolve: { extensions: [".ts", ".tsx"] }, module: { rules: [ { test: /\.tsx?$/, use: "ts-loader", exclude: /node_modules/ } ] },
The settings of the resolve and rules properties make webpack
look for TypeScript
files (.ts
and .tsx
) and process those files using the "ts-loader" TypeScript
transpiler loader.
Resolve Taipy GUI dependency¶
As you can see in the implementation code for React components, we depend on a
JavaScript library that Taipy GUI provides.
In the source code for the components, you will find directives similar to the
following:
import { ... } from "taipy-gui";
The "taipy-gui" module is provided by Taipy GUI, where it has been installed.
You can query where Taipy GUI was installed by issuing the command:
pip show taipy-gui
To simplify the understanding of the rest of this section, let's assume that you store
the directory path returned by this command in the environment variable 'TAIPY_GUI_DIR'.
To install the Taipy GUI JavaScript module, you need to run the following command:
npm i $TAIPY_GUI_DIR/taipy/gui/webapp
npm i %TAIPY_GUI_DIR%\taipy\gui\webapp
package.json
file that is installed in Taipy GUI (in
$TAIPY_GUI_DIR/taipy/doc/extension/example_library/front-end
), there is an entry
called "install" in the "scripts" section that invokes an NPM script to perform
this task.
This dependency must be explicitly declared for webpack
to use as an external
library.
In the webpack.config.js
file you can find the following lines, that assumes
that the environment variable "TAIPY_GUI_DIR" was set as indicated above:
externals: {"taipy-gui": "TaipyGui"},
plugins: [
new webpack.DllReferencePlugin({
manifest: path.resolve(
__dirname,
`${process.env.TAIPY_GUI_DIR}/taipy/gui/webapp/taipy-gui-deps-manifest.json`
),
name: "TaipyGuiDependencies"
}),
]
Bundle the JavaScript code¶
webpack
must be configured to indicate where the front-end JavsScript is produced.
This is done by setting the output property in webpack.config.js
:
output: {
filename: "extensionLibrary.js",
path: path.resolve(__dirname, "dist"),
library: {
// Camel case transformation of the library name
name: "MyExtensionLibrary",
type: "umd"
},
},
When the different configuration files are up-to-date, building the extension library bundle is just a matter of running:
npm run build
Debug mode
To debug your front-end code in the browser, you will want to use the command:
npm run build:dev
This preserves the symbols in the module file and generates a map file next to it, allowing for stepping in your TypeScript source code even after transpilation.
With the settings listed above, the result of this command should be a file called
extensionLibrary.js
under the dist
directory.
This path, relative to the Python file where the element library is defined, must
appear in the value returned by the method get_scripts()
of
the ElementLibrary
subclass.
Property types¶
Elements properties are what control the rendering and the behavior of front-end
components.
The Taipy GUI Extension API exposes functions that allow components to consume
data coming from the application and send messages to the Python code that
can then handle user interactions.
The API depends on the type of the properties you need to implement.
Here are the sections that address the different use cases that your custom
element may need: