Using WebAssetsManager Joomla 4 and Adding Your Own Presets Using a Plugin

In the frontend world, many resources (assets) are interconnected. Joomla never had an easy way to specify this relationship, but Joomla 4 changed that by introducing the concept Web Assets. Managing JavaScript and CSS in Joomla has been greatly simplified thanks to the class WebAssetManager. There is a great article How to properly include JavaScript and CSS in Joomla 4, which explains this concept and its application in detail and with code examples. I recommend reading it for a more complete understanding of the essence of this article.

However, in the process of developing my own solutions, I ran into a problem. Its solution in this note will be a small addition to the above article.

A task

The task is to connect the js library (in my case – Swiper.js) to the common registry of scripts, styles and presets in such a way that it is available from various places in Joomla 4 and can be used for different extensions (possibly not only mine), update it autonomously.

For example: a js-library is connected by a plugin, and it can be used by a module, a component, a content plugin, and a field plugin.

Behavior is built into Joomla 4 Bootstrap 5. It supports modular connection with automatic connection of all dependencies, which are described in the file media/vendor/joomla.asset.json.

Joomla 4 will look for asset definitions automatically at run time in the following order:

It will then load them into a registry of known JavaScript and CSS files.

You can’t edit kernel files (although, unfortunately, this is common among developers who want to quickly solve some problem). So we need a plugin that can “crawl” at the stage of forming the registry of assets and add the web assets we need to it. This decision was immediately obvious.

However, all examples of creating and connecting scripts, styles and presets assumed that the asset was registered and started to be used in the same place. Attempts to move the connection of the asset to the plugin, and use the asset in the module through $wa->usePreset, $wa->useScript led to an error "There is no "swiper-bundle" asset of a "script" type in the registry."

In order to understand how the system works, I began to analyze the code of “boxed” extensions.

Example from jooally system plugin – visually impaired version plugin

<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.jooa11y
 *
 * @copyright   (C) 2021 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\Application\CMSApplicationInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;

/**
 * Jooa11y plugin to add an accessibility checker
 *
 * @since  4.1.0
 */
class PlgSystemJooa11y extends CMSPlugin implements SubscriberInterface
{
	/**
	 * Application object.
	 *
	 * @var    CMSApplicationInterface
	 * @since  4.1.0
	 */
	protected $app;

	/**
	 * Affects constructor behavior. If true, language files will be loaded automatically.
	 *
	 * @var    boolean
	 * @since  4.1.0
	 */
	protected $autoloadLanguage = true;

	/**
	 * Subscribe to certain events
	 *
	 * @return string[]  An array of event mappings
	 *
	 * @since 4.1.0
	 *
	 * @throws Exception
	 */
	public static function getSubscribedEvents(): array
	{
		$mapping = [];

		// Срабатываем только на фронте
		if (Factory::getApplication()->isClient('site'))
		{
    /**
     * Срабатываем на событие onBeforeCompileHead и вызываем функцию initJooa11y.
     * Можно по старинке упростить и использовать public function onBeforeCompileHead()
     */
			$mapping['onBeforeCompileHead'] = 'initJooa11y';
		}

		return $mapping;
	}
  }

The plugin fires on an event onBeforeCompileHead. At this event, it is possible to process everything that makes up the content <head> pages in Joomla 4: title, meta tags, etc.

In the function itself initJooa11y there are checks for CLI, REST API – so that the plugin works only when HTML is output. At the very end of the function, the registration and addition of scripts and styles for the functioning of the version for the visually impaired takes place:

// Get the document object.
		$document = $this->app->getDocument();
    
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa*/
		$wa = $document->getWebAssetManager();

		$wa->getRegistry()->addRegistryFile('media/plg_system_jooa11y/joomla.asset.json');

		$wa->useScript('plg_system_jooa11y.jooa11y')
			->useStyle('plg_system_jooa11y.jooa11y');

In file media/plg_system_jooa11y/joomla.asset.json describes the files and their dependencies for the plugin to work. Please note that immediately after registering an asset, its use begins – useScript and useStyle.

But this behavior did not suit me, because I wanted to achieve greater universality and autonomy of the elements. Helped own searches and the response of the Joomla community. So….

How to add custom js and css to Joomla 4 and make them available globally?

Create a plugin group system. Official Documentation for Joomla 4 Developers for creating plugins. You can use the methods of the form public function onBeforeCompileHead(). In this case, the plugin can be used in both Joomla 3 and Joomla 4. Or you can use the new method proposed in Joomla 4.1.

defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;

class PlgSystemWtjswiper extends CMSPlugin implements SubscriberInterface
{
	/**
	 * Subscribe to certain events
	 *
	 * @return string[]  An array of event mappings
	 *
	 * @since 4.1.0
	 *
	 * @throws Exception
	 */
	public static function getSubscribedEvents(): array
	{
		$mapping = [];

		// Only trigger in frontend
		if (Factory::getApplication()->isClient('site'))
		{
			$mapping['onAfterInitialise'] = 'addSwiperPreset';
		}

		return $mapping;
	}
  }

This method, when used correctly and the event is called, allows you not to remember the order of the arguments passed.

The most important thing was to find the right system event, at the stage of which it is possible to add your web assets globally to the Joomla 4 Web Assets Manager. Such an event was onAfterInitialise. In my case, attempts to register a web asset on an event onBeforeCompileHead caused joomla.asset.json to be added to the asset registry but not parsed. Which is equivalent to the fact that it does not exist.

The following code allows you to register a javascript library

public function addSwiperPreset()
	{
		// Only trigger in frontend
		if (Factory::getApplication()->isClient('site'))
		{
			/** @var Joomla\CMS\WebAsset\WebAssetManager $wa*/
			$wa = Factory::getDocument()->getWebAssetManager();
			$wa->getRegistry()->addRegistryFile('media/plg_system_wtjswiper/joomla.asset.json');
			return true;
		}
	}

Pay attention to the method addRegistryFile()where the path to the file is specified joomla.assets.json from the site root. There is also a proxy method addExtensionRegistryFile(string $name)which takes as a parameter the system name of the extension by which its web assets are available in the folder media: com_content, plg_system_jooally etc. Then the file ‘media/com_content/joomla.asset.json’ and ‘media/plg_system_jooally/joomla.asset.json’ respectively.

File contents joomla.assets.json

{
  "$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json",
  "name": "swiper",
  "version": "8.2.4",
  "description": "Swiper js library",
  "license": "GPL-2.0-or-later",
  "assets": [
    {
      "name": "swiper-bundle",
      "type": "script",
      "uri": "plg_system_wtjswiper/swiper-bundle.min.js",
      "attributes": {
        "defer": true
      },
      "package": "swiper",
      "version": "8.2.4"
    },
    {
      "name": "swiper-bundle",
      "type": "style",
      "uri": "plg_system_wtjswiper/swiper-bundle.min.css",
      "package": "swiper",
      "version": "8.2.4"
    },
    {
      "name": "swiper-bundle",
      "type": "preset",
      "uri": "",
      "dependencies": [
        "swiper-bundle#style",
        "swiper-bundle#script"
      ]
    }
  ]
}

Uri in json, depending on the type of asset, is automatically completed with ‘js’ or ‘css’. If you include a file media/plg_system_wtjswiper/css/swiper-bundle.min.cssthen the uri of the file in joomla.assets.json will be plg_system_wtjswiper/swiper-bundle.min.css

The same principle was used earlier in Joomla 3 when connecting resources using HTMLHelper (ex. JHTML).

Also helpful resources

Community Resources:

Telegram:

Similar Posts

Leave a Reply