JavaScript Data Grid Custom plugins
Extend Handsontable's functionality by writing your custom plugin. Use the BasePlugin for a quick start.
In this article
Overview
Plugins are a great way of extending Handsontable's capabilities. In fact, most Handsontable features are provided by plugins.
1. Prerequisites
Import the following:
BasePlugin
- a built-in interface that lets you work within Handsontable's lifecycle,registerPlugin
- a utility to register a plugin in the Handsontable plugin registry.
import { BasePlugin, registerPlugin } from 'handsontable/plugins';
BasePlugin
2. Extend the The best way to start creating your own plugin is to extend the BasePlugin
.
The BasePlugin
interface takes care of:
- Backward compatibility
- Memory leak prevention
- Properly binding your plugin's instance to Handsontable
export class CustomPlugin extends BasePlugin {
/**
* Define a unique key (a string) for your plugin.
* The key becomes the plugin's alias.
* Handsontable registers the plugin under that alias.
* When an `updateSettings()` call includes the plugin's alias,
* your plugin's state gets updated.
* You can also use the alias to recognize the plugin's
* options passed through the Setting object at Handsontable's initialization.
*
* @returns {string}
*/
static get PLUGIN_KEY() {
return 'customPlugin';
}
/**
* Define additional setting keys (an array of strings) for your plugin.
* When an `updateSettings()` call includes at least one of those setting keys,
* your plugin's state gets updated.
* If you set SETTING_KEYS() to return `true`, your plugin updates on every `updateSettings()` call.
* If you set SETTING_KEYS() to return `false`, your plugin never updates on any `updateSettings()` call.
*
* @returns {Array|boolean}
*/
static get SETTING_KEYS() {
return true;
}
/**
* Extend the default constructor and define internal properties for your plugin.
*
* @param {Handsontable} hotInstance
*/
constructor(hotInstance) {
/**
* The [`BasePlugin`](/javascript-data-grid/api/base-plugin/)'s constructor adds a `this.hot` property to your plugin.
* The `this.hot` property:
* - Is a reference to the Handsontable instance.
* - Can't be overwritten.
* - Gives you access to columns' and rows' index mappers.
*/
super(hotInstance);
// Initialize all your public properties in the class' constructor.
this.configuration = {
enabled: false,
msg: ''
};
}
/**
* Unifies configuration passed to settings object.
*
* @returns {object}
* @throws {Error}
*/
getUnifiedConfig() {
const pluginSettings = this.hot.getSettings()[CustomPlugin.PLUGIN_KEY];
if (pluginSettings === true) {
return {
enabled: true,
msg: 'default msg boolean'
};
}
if (Object.prototype.toString.call(pluginSettings) === '[object Object]') {
return {
enabled: true,
msg: 'default msg obj',
...pluginSettings
};
}
if (pluginSettings === false) {
return {
enabled: false,
msg: ''
};
}
throw new Error(
`${CustomPlugin.PLUGIN_KEY} - incorrect plugins configuration.
Passed:
- type: ${typeof pluginSettings}
- value: ${JSON.stringify(pluginSettings, null, ' ')}
Expected:
- boolean
- object
`
);
}
/**
* Checks if the plugin is enabled in the settings.
*/
isEnabled() {
const pluginSettings = this.getUnifiedConfig();
return pluginSettings.enabled;
}
/**
* The `enablePlugin` method is triggered on the `beforeInit` hook.
* It should contain your plugin's initial setup and hook connections.
* This method is run only if the `isEnabled` method returns `true`.
*/
enablePlugin() {
// Get the plugin's configuration from the initialization object.
this.configuration = this.getUnifiedConfig();
// Add all your plugin hooks here. It's a good idea to use arrow functions to keep the plugin as a context.
this.addHook('afterChange', (changes, source) => this.onAfterChange(changes, source));
// The `super` method sets the `this.enabled` property to `true`.
// It is a necessary step to update the plugin's settings properly.
super.enablePlugin();
}
/**
* The `disablePlugin` method disables the plugin.
*/
disablePlugin() {
// Reset all of your plugin class properties to their default values here.
this.configuration = null;
// The `BasePlugin.disablePlugin` method takes care of clearing the hook connections
// and assigning the 'false' value to the 'this.enabled' property.
super.disablePlugin();
}
/**
* The `updatePlugin` method is called on the `afterUpdateSettings` hook
* (unless the `updateSettings` method turned the plugin off),
* but only if the config object passed to the `updateSettings` method
* contains entries relevant to that particular plugin.
*
* The `updatePlugin` method should contain anything that your plugin needs to do to work correctly
* after updating the Handsontable instance settings.
*/
updatePlugin() {
// The `updatePlugin` method needs to contain all the code needed to properly re-enable the plugin.
// In most cases simply disabling and enabling the plugin should do the trick.
const { enabled, msg } = this.getUnifiedConfig();
// You can decide if updating the settings triggers the the disable->enable routine or not.
if (enabled === false && this.enabled === true) {
this.disablePlugin();
} else if (enabled === true && this.enabled === false) {
this.enablePlugin();
}
// If you need to update just a single option.
if (this.configuration !== null && msg && this.configuration.msg !== msg) {
this.configuration.msg = msg;
}
super.updatePlugin();
}
/**
* Define your external methods.
*/
externalMethodExample() {
// Method definition.
}
/**
* The afterChange hook callback.
*
* @param {CellChange[]} changes An array of changes.
* @param {string} source Describes the source of the change.
*/
onAfterChange(changes, source) {
// afterChange callback goes here.
console.log(
`${CustomPlugin.PLUGIN_KEY}.onAfterChange - ${this.configuration.msg}`,
changes,
source
);
}
/**
* The `destroy` method is the best place to clean up any instances,
* objects or index mappers created during the plugin's lifecycle.
*/
destroy() {
// The `super` method cleans up the plugin's event callbacks, hook connections, and properties.
super.destroy();
}
}
3. Register CustomPlugin
Now, register your plugin.
There are two ways to register a plugin:
- Option 1: Define a static getter named
PLUGIN_KEY
that theregisterPlugin
utility uses as the plugin's alias. Check the example in the code snippet above.// You need to register your plugin in order to use it within Handsontable. registerPlugin(CustomPlugin);
- Option 2: Use a custom alias. Put a string in the first argument. The registerer uses that string as an alias, instead of the
PLUGIN_KEY
getter fromCustomPlugin
.registerPlugin('CustomAlias', CustomPlugin);
4. Use your plugin in Handsontable
To control the plugin's options, pass a boolean or an object at the plugin's initialization:
import Handsontable from 'handsontable';
import { CustomPlugin } from './customPlugin';
const hotInstance = new Handsontable(container, {
// Pass `true` to enable the plugin with default options.
[CustomPlugin.PLUGIN_KEY]: true,
// You can also enable the plugin by passing an object with options.
[CustomPlugin.PLUGIN_KEY]: {
msg: 'user-defined message',
},
// You can also initialize the plugin without enabling it at the beginning.
[CustomPlugin.PLUGIN_KEY]: false,
});
5. Get a reference to the plugin's instance
To use the plugin's API, call the getPlugin
method to get a reference to the plugin's instance.
const pluginInstance = hotInstance.getPlugin(CustomPlugin.PLUGIN_KEY);
pluginInstance.externalMethodExample();
Related API reference
- APIs:
- Core methods:
- Hooks:
← Modules Custom builds →