J4.x

J4.x:Web Assets

From Joomla! Documentation

Revision as of 14:01, 27 May 2019 by FuzzyBot (talk | contribs) (Updating to match new version of source page)
Other languages:
Deutsch • ‎English • ‎Nederlands • ‎français • ‎italiano • ‎русский

Concept

In the frontend world many assets are related. For example our keepalive script depends on the core.js file for options management. In Joomla there never was an easy way to specify this, you just had to include multiple files. Joomla 4 changes this with the concept of web assets.

Definition

Related assets are defined in a JSON file such as https://github.com/joomla/joomla-cms/blob/4.0.0-alpha7/build/media_source/system/joomla.asset.json#L13-L21

This has a structure of having a name, version, license and then one or more asset definitions. Assets are comprised of a list of js files and css files related to the assets and any dependencies. The dependencies section is just a list of asset names that are required for the asset to function. Example:

{
  "name": "com_example",
  "version": "4.0.0",
  "description": "Joomla CMS",
  "license": "GPL-2.0+",
  "assets": [
    {
      "name": "bar",
      "js": [
        "com_example/bar.js"
      ],
      "css": [
        "com_example/bar.css"
      ]
    },
    {
      "name": "beer",
      "dependencies": [
        "core",
        "bar"
      ],
      "js": [
        "com_example/beer.js"
      ],
      "attribute": {
        "com_example/beer.js": {
            "defer": true,
            "data-foo": "bar"
        }
      }
    }
  ]
}

Joomla! will look for next assets definition automatically at runtime (in following order):

media/vendor/joomla.asset.json (on first access to WebAssetRegistry)
media/system/joomla.asset.json
media/legacy/joomla.asset.json
media/{com_active_component}/joomla.asset.json (on dispatch the application)
templates/{active_template}/joomla.asset.json

Note: Each following assets definition will override assets from previous assets definition, by an asset name.

All known assets loaded and then stored in WebAssetRegistry, however to enable/disable an asset you have to use WebAssetManager. You can register your own assets definition via WebAssetRegistry:

$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wr = $wa->getRegistry();
$wr->addRegistryFile('relative/path/to/your/joomla.asset.json');

To add a custom asset at runtime:

$wr->add(new Joomla\CMS\WebAsset\WebAssetItem('foobar', ['js' => ['media/foobar/file.js']]));

To check whether an asset exists:

if ($wr->exists('foobar'))
{
    var_dump('Asset "foobar" exists!');
}


Enabling an asset

All asset management in the current Document handled by WebAssetManager, which is accessible with $doc->getWebAssetManager(); By using AssetManager you can enable or disable needed asset easily in Joomla! through a standard methods.

To enable an asset in the page use the enableAsset function, for example:

$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wa->enableAsset('keepalive');
// Or multiple
$wa->enableAsset('keepalive')->enableAsset('fields.validate')->enableAsset('foobar');

WebAssetManager will look to WebAssetRegistry whether the requested asset exists, and will enable it for current Document instance. Otherwise it will throw an UnknownAssetException.

To disable an asset in the page use the disableAsset function. The example below will disable the jquery-noconflict asset from being loaded.

$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wa->disableAsset('jquery-noconflict');

Note: If there are any dependencies to the disabled asset, then this asset will be re-enabled automatically, no matter what.

To check whether asset enabled, and the asset state:

// Checking whether an asset are active (enabled manually or automatically as dependency)
if ($wa->isAssetActive('foobar'))
{
    var_dump('Asset "foobar" are active!');
}

// Checking state
switch($wa->getAssetState('foobar')){
	case Joomla\CMS\WebAsset\WebAssetManager::ASSET_STATE_ACTIVE:
		var_dump('Active! Was enabled manually');
		break;
	case Joomla\CMS\WebAsset\WebAssetManager::ASSET_STATE_DEPENDENCY:
		var_dump('Active! Was enabled automatically while resolving dependencies');
		break;
	default:
		var_dump('not active!');
}


Advanced: Custom WebAssetItem class

The default class for all WebAsset items is Joomla\CMS\WebAsset\WebAssetItem.

You are also allowed to use a custom class, which must implement Joomla\CMS\WebAsset\WebAssetItemInterface or extend Joomla\CMS\WebAsset\WebAssetItem.

A custom class can allow you to do advanced actions, for example, including a script file depending on an active language:

class MyComExampleAssetItem extends WebAssetItem
{
	public function getScriptFiles($resolvePath = true): array
	{
		$files   = parent::getScriptFiles($resolvePath);
		$langTag = Factory::getApplication()->getLanguage()->getTag();
		$file    = 'com_example/bar-' . $langTag . '.js';

		if ($resolvePath)
		{
			$resolved = $this->resolvePath($file, 'script');
			if ($resolved['fullPath'])
			{
				$file = $resolved['fullPath'];
			}
		}

		$files[$file] = [];

		return $files;
	}
}

Additionally, implementing Joomla\CMS\WebAsset\WebAssetAttachBehaviorInterface allows you to add a script options (which may depend on the environment) when your asset is enabled and attached to the Document.

class MyFancyFoobarAssetItem extends WebAssetItem implements WebAssetAttachBehaviorInterface
{
	public function onAttachCallback(Document $doc): void
	{
		$user = Factory::getApplication()->getIdentity();
		$doc->addScriptOptions('com_example.fancyfoobar', ['userName' => $user->username]);
	}
}

Defining a custom WebAssetItem class in joomla.asset.json

In joomla.asset.json you can define which Class should be used with specific AssetItem. For this you can use 2 properties namespace and class. namespace can be defined at Root level (then it will be used as default namespace for all Asset items in joomla.asset.json) or in the Item level. For example:

{
  "name": "com_example",
  "version": "4.0.0",
  "namespace": "Joomla\Component\Example\WebAsset",
  "assets": [
    {
      "name": "foo",
      "class": "FooAssetItem",
      "js": [....]
    },
    {
      "name": "bar",
      "namespace": "MyFooBar\Library\Example\WebAsset",
      "class": "BarAssetItem",
      "js": [....]
    }
  ]
}

Here the asset foo will be associated with class Joomla\Component\Example\WebAsset\FooAssetItem, and bar with class MyFooBar\Library\Example\WebAsset\BarAssetItem.


Note: If namespace are not defined then by default will be used Joomla\CMS\WebAsset. When namespace is defined but empty, then no namespace will be used, only class. Example:

{
  "name": "com_example",
  "assets": [
    {
      "name": "foo",
      "class": "FooAssetItem",
      "js": [....]
    },
    {
      "name": "bar",
      "namespace": "",
      "class": "BarAssetItem",
      "js": [....]
    }
  ]
}

Here the asset foo will be associated with class Joomla\CMS\WebAsset\FooAssetItem, and bar with class BarAssetItem (without namespace).