Plugins
Presentation
Plugins bind JavaScript code to a string. They allow to refer to a set of properties and methods through a plugin key. It's the mechanism used to make customizations serializable and thus persistable. Together with settings, plugins are the main extension point of ActiveUI.
Usage
Registering a Plugin Implementation
In order to register your own plugin implementation, use for instance:
const activeUI = createActiveUI({
plugins: {
// `cell-editor` is the plugin type.
'cell-editor': [
// Array of `cell-editor` plugins you wish to register.
{
key: 'my-cell-editor',
createProperties(parameters) {
return {
myProperty(cell) {
// do stuff
},
};
},
staticProperties: {
myStaticProperty: true,
},
},
],
},
});
The plugins
option is a map with plugin types as keys and lists of PluginDescription
as values.
Referring to a Plugin Implementation
Plugins are often referred to within the configuration of a widget, for example a tabular view or a pivot table. Within that configuration, a plugin can be referred to in two ways: as a string or as an object.
When no arguments are required, there are three equivalent ways of referring to a plugin:
// Referring to 'my-cell-editor' as a string
const tabularViewConfiguration = {
cellEditors: ['my-cell-editor'],
};
// Referring to 'my-cell-editor' as an object with no parameters
const tabularViewConfiguration = {
cellEditors: [
{
key: 'my-cell-editor',
},
],
};
// Referring to 'my-cell-editor' as an object with no parameters (empty args)
const tabularViewConfiguration = {
cellEditors: [
{
key: 'my-cell-editor',
args: {},
},
],
};
When a plugin accepts or requires parameters, it can be referred to as:
// Referring to 'my-cell-editor' as an object with parameters
const tabularViewConfiguration = {
cellEditors: [
{
key: 'my-cell-editor',
args: {arg1: 'myArgument1', arg2: 42},
},
],
};
Note: The two ways are always valid, but only the second one allows to pass parameters to the plugin.
Available Plugins
The list of available plugins is here.
More Details About the Plugin Concept
The Global State
The architecture of ActiveUI is based on a global state, which is a single plain JavaScript object containing all the information needed to render the page. Building an application with such an architecture has a lot of theoretical benefits:
- Undo/redo history: undo/redo is easily implemented as reloading a previous state object.
- Save and restore application state: it is possible to reload the application in the same state you left it because the state cab be stored in the browser persisted storage.
- Dynamic localization/theming: it enables re-rendering of the application with a different locale and theme, without having to reload the page.
- Collaboration: pieces of state could be synchronized through a remote server in order to "screen share" and collaborate with someone else's session on a specific widget.
- Bookmarking: each widget become "bookmarkable" as they are represented by a single plain JavaScript object that can thus be serialized as JSON.
- Bug reproduction: if something went wrong with the application, the global state can be dumped and attached to the issue along with a screenshot in order to understand and reproduce the issue; i.e. the current state dump could be sufficient to reproduce a bug.
- Advanced tweaking: it is possible to directly write into the widget's state to update some parameters that have, for instance, no corresponding UI yet.
The Issue
So this large plain JavaScript object is convenient because we have a human readable JSON that completely describes the state of the application. Now, this has an obvious implication: every piece of information we need to render must be storable in this plain JavaScript object and therefore must be serializable. So it has to be a tree of primitive types like booleans, strings, numbers, arrays, or hashes. This excludes one main type in JavaScript: functions. Functions cannot be converted to a serializable representation because they are more than the string representation of their body: they often refer to external variables (called closures).
An Example
Let's say you would like to override what happens when a user double clicks on a cell of a tabular view. The natural way of describing this would be to give a function to our tabular view configuration and we would execute this function when double clicking on a cell. We can't do that because at the time the tabular view is displayed, the only source of information we can read from is the global state which cannot contain functions.
The Solution
This is where plugins comes into play. The principle is that, in your project code, you can register objects with methods by giving them a key, and then these keys can be used inside the global state to refer to the function. This has a few impact regarding the list of benefits of the global state:
- Collaboration: will work only if both users are using the same project code. Otherwise they might have different implementations for the same plugin key which will lead to inconsistencies.
- Bug reproduction: we might need to have the code of your project plugins in addition to the global state if they are referred in a place related to the bug.