RequireJS in backend modules

javascript tutorial

3 minutes, 34 seconds

Since TYPO3 CMS 7 it is possible to use RequireJS in the backend. A short explanation: RequireJS enables developers to have some kind of dependency handling for JavaScript. The JavaScript is written as so-called "Asynchronous Module Definition" (AMD). Some libraries delivered with TYPO3 are written as modules. This tutorial explains how you can write such modules on your own.

To be able to use RequireJS at all, some prerequisites must be fulfilled:

  • Your extension must have a Resources/Public/JavaScript directory. That directory is used for autoloading
  • Think about what's the purpose of the module. You can only write one module per file (anything else is bad practice anyway)

For this tutorial our extension name is foo_bar and we name our module Wisdom. So, create the file typo3conf/ext/foo_bar/Resources/Public/JavaScript/Wisdom.js.

The module's namespace is now TYPO3/CMS/FooBar/Wisdom. As you can see, the extension name is upper camelcased.

Every AMD is wrapped in the same construct:

define([], function() {
});

This is the "container" of the module. It holds the module logic and takes care of dependencies.

Core-wise, TYPO3 itself defines in its own modules an object holding the module logic, namely parameters and methods. The object has the same name as the module, in our case "Wisdom":

define([], function() {
    var Wisdom = {
        sayings: [
            'The quick brown fox jumps over the lazy dog',
            'Pack my box with five dozen liquor jugs',
            'The five boxing wizards jump quickly',
            'Jackdaws love my big sphinx of quartz',
            'How vexingly quick daft zebras jump',
            'Bright vixens jump; dozy fowl quack'
        ]
    };

    Wisdom.say = function() {
        alert(Wisdom.sayings[Math.floor(Math.random() * Wisdom.sayings.length)]);
    };
});

The module is now enriched with some logic. But something is missing: We have to decide whether the module should be called when a page is loaded or it should be used in another module.

You may let the module run on load and return the object for further usage, but that is rather useless.

In any way, the missing part is a piece of cake. To let the module be a dependency of another module, add

return Wisdom;

To trigger the module after the page was loaded, add the ready listener:

$(function() {
    Wisdom.say();
});

The above code can be shortened by using $(Wisdom.say);

Dependency Handling

In the current state you may get a JavaScript error telling that "$ is undefined". This is correct, because $ is unknown in this context, yet. Which leads to the "dependency" thing.

You may wonder what that empty array in define() is all about. This array holds the modules that are loaded beforehand. To get jQuery working, the line must be adjusted:

define(['jquery'], function($) {

But why was the $ parameter added to the function? Because the dependency array goes hand in hand with the parameter list of the function, each dependency is accessible by the name passed as argument in the function. You might name the parameter jq, jQuery would be accessibly by jq then.

Some libraries in the TYPO3 Core are loaded as dependency, but are not callable, because they e.g. extend jQuery. In such case, the libraries are at the end of the array and receive no parameter name.

define(['jquery', 'TYPO3/CMS/Backend/jquery.clearable'], function($) {

TYPO3/CMS/Backend/jquery.clearable is now loaded as dependency, but is not callable, as there is no second parameter in the function.

You may now write another module that uses our "Wisdom" module as dependency:

define(['jquery', 'TYPO3/CMS/FooBar/Wisdom'], function($, Wisdom) {

Please keep in mind that the "Wisdom" module in such case module return Wisdom instead of using the ready event.

Use after loading

In case you use the ready event, you may wonder how to use the module. Answer: it depends! If you use Fluid's f:be.pageRenderer view helper, simply add the argument includeRequireJsModules:

<f:be.pageRenderer
    includeRequireJsModules="{
        0:'TYPO3/CMS/FooBar/Wisdom'
    }"
    />

However, if you don't use Fluid you may use PageRenderer:

$pageRenderer = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Page\PageRenderer::class);
$pageRenderer->loadRequireJsModule('TYPO3/CMS/FooBar/Wisdom');

Bonus: loadRequireJsModule takes a second argument $callBackFunction which is executed right after the module was loaded. The callback function must be wrapped within function() {}:

$pageRenderer->loadRequireJsModule(
    'TYPO3/CMS/FooBar/Wisdom',
    'function() { console.log("Loaded Wisdom."); }'
);

Next Post