How to use @drupal/once in the right way
Why do we need @drupal/once
We need the @drupal/once
library to ensure that the code runs exactly once. Since Drupal Behaviors are run every time Drupal Ajax is executed, we need to make sure the code runs one time.
Previously this was done by the jQuery.once
library, now we have its native alternative @drupal/once
that is available in Drupal core since 9.2.
How to use @drupal/once
Add core/once
library as a dependency to Drupal library:
my-library:
dependencies:
- core/once
To allow global function in eslint
, add this to your .eslintrc.json
file:
{
"globals": {
"once": true
}
}
Examples of use
Find one element:
const [editorMenu] = once('editor-menu', '.editor-menu', context)
if (editorMenu) {
console.log(editorMenu)
}
Find several elements:
// Using Array.foreach()
const editorMenuItems = once('editor-menu-items', '.menu-item', context)
editorMenuItems.forEach(item, index) => {
console.log(item, index)
}
// or using for...of
const editorMenuItems = once('editor-menu-items', '.menu-item', context)
for (item of editorMenuItems) {
console.log(item)
}
Vscode snippets
For the VSCode editor, we can prepare custom snippets so that we don't have to write once
wrapper every time by hand:
{
"once drupal behavior (one element)": {
"prefix": "oncedrupal",
"body": "const [${1:element}] = once('${2:once-string}', '${3:selector}', context);\nif (${1:element}) {\n// code \n}",
"description": "once drupal behavior (one element)"
},
"once drupal behavior (array foreach)": {
"prefix": "oncedrupal",
"body": "const ${1:array} = once('${2:once-string}', '${3:selector}', context);\n${1:array}.forEach(${4:element} => {\n// code \n});",
"description": "once drupal behavior (array foreach)"
},
"once drupal behavior (array for of)": {
"prefix": "oncedrupal",
"body": "const ${1:array} = once('${2:once-string}', '${3:selector}', context);\nfor (const ${4:element} of ${1:array}) {\n// code \n};",
"description": "once drupal behavior (array for of)"
}
}
References:
Last Updated: