J4.x

J4.x: risorse Web

From Joomla! Documentation

This page is a translated version of the page J4.x:Web Assets and the translation is 27% complete.

Outdated translations are marked like this.
Other languages:
Deutsch • ‎English • ‎français • ‎italiano

Concetto

Nel mondo del frontend molte risorse sono correlate. Ad esempio, il nostro script keepalive dipende dal file core.js per la gestione delle opzioni. In Joomla non c'è mai stato un modo semplice per specificare questo, dovevi solo includere più file. Joomla 4 cambia questo con il concetto di risorse web.

Definizione

Le risorse correlate sono definite in un file JSON come https://github.com/joomla/joomla-cms/blob/4.0.0-alpha7/build/media_source/system/joomla.asset.json#L13-L21

Questo ha una struttura con una definizione dello schema (per la convalida), nome, versione, licenza e quindi una o più definizioni delle risorse. Le risorse sono costituite da un elenco di file js e file css relativi alle risorse e alle eventuali dipendenze. La sezione delle dipendenze è solo un elenco di nomi di risorse necessari per il funzionamento della risorsa. Esempio:

{
  "$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json",
  "name": "com_example",
  "version": "4.0.0",
  "description": "Joomla CMS",
  "license": "GPL-2.0+",
  "assets": [
    {
      "name": "bar",
      "type": "style",
      "uri": "com_example/bar.css"
    },
    {
      "name": "bar",
      "type": "script",
      "uri": "com_example/bar.js"
    },
    {
      "name": "beer",
      "type": "style",
      "uri": "com_example/beer.css",
      "dependencies": [
        "bar"
      ],
    },
    {
      "name": "beer",
      "type": "script",
      "dependencies": [
        "core",
        "bar"
      ],
      "uri": "com_example/beer.js",
      "attributes": {
        "defer": true,
        "data-foo": "bar"
      }
    }
  ]
}

The $schema attribute is a schema definition file that allows you to validate your file using JSON Schema. Read the official website for more information on json schema validation works.

Note: Having joomla.asset.json for your extension or template are recommend but not required to WebAssset to work (see next section).

Note: It is not recommended to add an inline asset to a json file, prefer to use a file.

Explaining asset stages

Each asset has 2 stages: registered and used.

Registered is where an asset is loaded into WebAssetRegistry. That means WebAssetManager knows about the existence of these assets, but will not attach them to a document while rendering. All assets loaded from joomla.asset.json is at registered stage.

Used is where an asset is enabled via "$wa->useAsset()" (->useScript(), ->useStyle(), ->registerAndUseX() etc). That means WebAssetManager will attach these assets and their dependencies to a document while rendering.

An asset cannot be used if it was not registered before, this will cause an unknown asset exception.

Register an asset

All known assets loaded and then stored in WebAssetRegistry (to enable/disable an asset item you have to use WebAssetManager, see next section).

Joomla! cercherà automaticamente la prossima definizione delle risorse in fase di esecuzione (nel seguente ordine):

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

And load them to registry of known assets.

Nota: Ogni definizione di asset seguente sostituirà gli asset dalla definizione di asset precedente, con un nome di asset.


Puoi registrare la tua definizione di risorse tramite WebAssetRegistry:

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wr = $wa->getRegistry();
$wr->addRegistryFile('relative/path/to/your/joomla.asset.json');

Per aggiungere una risorsa personalizzata in fase di esecuzione:

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

Or more simply, using WebAssetManager:

$wa->registerScript('foobar', 'com_foobar/file.js');

The new asset item foobar will be added to the registry of know assets, but will not be attached to a document until your code (a layout, template etc) will request it.

Per verificare se esiste una risorsa:

if ($wa->assetExists('script', 'foobar'))
{
    var_dump('Script "foobar" exists!');
}


Abilitazione di una risorsa

Tutta la gestione delle risorse nel documento corrente gestita da WebAssetManager, accessibile con $ doc->getWebAssetManager (); Utilizzando AssetManager puoi abilitare o disabilitare facilmente le risorse necessarie in Joomla! attraverso metodi standard.

Per abilitare una risorsa nella pagina, utilizzare la funzione enableAsset, ad esempio:

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wa->useScript('keepalive');

// Or multiple
$wa->useScript('keepalive')
    ->useScript('fields.validate')
    ->useStyle('foobar')
    ->useScript('foobar');

// Add new asset item with dependency and use it
$wa->registerAndUseScript('bar', 'com_foobar/bar.js', [], [], ['core', 'foobar']);

WebAssetManager cercherà WebAssetRegistry se la risorsa richiesta esiste e la abiliterà per l'istanza del documento corrente. Altrimenti genererà UnknownAssetException.

Per disabilitare una risorsa nella pagina utilizzare la funzione disableAsset. L'esempio seguente disabiliterà il caricamento dell'asset jquery-noconflict.

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wa->disableScript('jquery-noconflict');

Nota: Se ci sono dipendenze per la risorsa disabilitata, questa risorsa verrà riattivata automaticamente, non importa quale.

Per verificare se la risorsa è abilitata e lo stato della risorsa:

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

// Checking state
switch($wa->getAssetState('script', '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!');
}


Overriding an asset

Overriding may be useful when you need to redefine the URI of asset item or its dependencies.

As already was noted, each of the following assets definition from joomla.asset.json will override asset items from previous assets definitions, by item name.

That means if you provide joomla.asset.json which contain already loaded asset items, they will be replaced with your items.

Another way to override in the code is to register an item with the same name.

Example, we have "foobar" script, that load com_example/foobar.js library, and we want to use CDN for this exact library:

How it defined in the system initially:

...
{
  "name": "foobar",
  "type": "script",
  "uri": "com_example/foobar.js",
  "dependencies": ["core"]
}
...

To override the URI we define the asset item with "foobar" name in our joomla.asset.json:

...
{
  "name": "foobar",
  "type": "script",
  "uri": "http://foobar.cdn.blabla/foobar.js",
  "dependencies": ["core"]
}
...

Or, register new asset item with AssetManager:

$wa->registerScript('foobar', 'http://fobar.cdn.blabla/foobar.js', [], [], ['core']);

Working with a styles

AssetManager allow to manage Stylesheet files. Stylesheet asset item have a type "style".

Example json defination of item in joomla.asset.json:

...
{
  "name": "foobar",
  "type": "style",
  "uri": "com_example/foobar.css"
}
...

Methods to work with styles

AssetManager offers the following methods to work with style files:

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();

// Attach foobar to the document
$wa->useStyle('foobar');

// Disable foobar from being attached
$wa->disableStyle('foobar');

// Register custom item without json defination
$wa->registerStyle('bar', 'com_example/bar.css', [], ['data-foo' => 'some attribute'], ['some.dependency']);
// And use it later
$wa->useStyle('bar');

// Register and attach a custom item in one run
$wa->registerAndUseStyle('bar', 'com_example/bar.css', [], ['data-foo' => 'some attribute'], ['some.dependency']);

Add inline style

Additionaly to style files, WebAssetManager allows you to add an inline style, and maintain their relation to the file asset. Inline styles may be placed directly before the dependency, after the dependency, or as usual after all styles.

Inline asset may have a name as well as other assets (but not required), the name can be used to retrieve the asset item from a registry, or as a dependency to another inline asset. If the name is not specified then a generated name based on a content hash will be used.

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();

// Add an inline content as usual, will be rendered in flow after all assets
$wa->addInlineStyle('content of inline1');

// Add an inline content that will be placed after "foobar" asset
$wa->addInlineStyle('content of inline2', ['position' => 'after'], ['data-foo' => 'bar'], ['foobar']);

// Add an inline content that will be placed before "foobar" asset
$wa->addInlineStyle('content of inline3', ['position' => 'before'], [], ['foobar']);

// Named inline asset
$wa->addInlineStyle('content of inline4', ['name' => 'my.inline.asset']);

Note: "foobar" asset should exist in the asset registry, otherwise you will get an unsatisfied dependency exception.

Example above will produce:

...
<style>content of inline3</style>
<link rel="stylesheet" href="foobar.css" />
<style data-foo="bar">content of inline2</style>
...
...
<style>content of inline1</style>
<style>content of inline4</style>
...

If inline asset has multiple dependencies, then will be used last one for positioning. Example:

$wa->addInlineStyle('content of inline1', ['position' => 'before'], [], ['foo', 'bar']);
$wa->addInlineStyle('content of inline2', ['position' => 'after'], [], ['foo', 'bar']);

Will produce:

...
<link rel="stylesheet" href="foo.css" />
<style>content of inline1</style>
<link rel="stylesheet" href="bar.css" />
<style>content of inline2</style>
...

Note: Named inline assets may be a dependency to another inline asset, however it is not recommended to use an inline asset as dependency to non-inline asset. This will work, but this behavior may change in the future. Prefer to use "position" instead.

Working with a scripts

AssetManager allow to manage Script files. Script asset item have a type "script".

Example json defination of item in joomla.asset.json:

...
{
  "name": "foobar",
  "type": "script",
  "uri": "com_example/foobar.js",
  "dependencies": ["core"]
}
...

Methods to work with scripts

AssetManager offer next methods to work with script files:

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();

// Attach foobar to the document
$wa->useScript('foobar');

// Disable foobar from being attached
$wa->disableScript('foobar');

// Register custom item without json defination
$wa->registerScript('bar', 'com_example/bar.js', [], ['defer' => true], ['core']);
// And use it later
$wa->useScript('bar');

// Register and attach a custom item in one run
$wa->registerAndUseScript('bar','com_example/bar.js', [], ['defer' => true], ['core']);

Add inline script

Addittionaly to script files WebAssetManager allow to add an inline script, and maintain their relation to the file asset. Inline script may be placed directly before the dependency, after the dependency, or as usual after all scripts.

Inline asset may have a name as well as other assets (but not required), the name can be used to retrive the asset item form a registry, or as dependecy to another inline asset. If name not specified then will be used generated name based on a content hash.

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();

// Add an inline content as usual, will be rendered in flow after all assets
$wa->addInlineScript('content of inline1');

// Add an inline content that will be placed after "foobar" asset
$wa->addInlineScript('content of inline2', ['position' => 'after'], ['data-foo' => 'bar'], ['foobar']);

// Add an inline content that will be placed before "foobar" asset
$wa->addInlineScript('content of inline3', ['position' => 'before'], [], ['foobar']);

// Named inline asset
$wa->addInlineScript('content of inline4', ['name' => 'my.inline.asset']);

// Specify script type
$wa->addInlineScript('content of inline5', [], ['type' => 'module']);

Note: "foobar" asset should exist in the asset registry, otherwise you will get an unsatisfied dependency exception.

Example above will produce:

...
<script>content of inline3</script>
<script src="foobar.js"></script>
<script data-foo="bar">content of inline2</script>
...
...
<script>content of inline1</script>
<script>content of inline4</script>
<script type="module">content of inline5</script>
...

If inline asset have a multiple dependencies, then will be used last one for positioning. Example:

$wa->addInlineScript('content of inline1', ['position' => 'before'], [], ['foo', 'bar']);
$wa->addInlineScript('content of inline2', ['position' => 'after'], [], ['foo', 'bar']);

Will produce:

...
<script src="foo.js"></script>
<script>content of inline1</script>
<script src="bar.js"></script>
<script>content of inline2</script>
...

Note: Named inline asset may be as dependency to another inline asset, hovewer it is not recomended to use an inline asset as dependency to non-inline asset. This will work, but this behavior may changes in future. Prefer to use "position" instead.

Working with a web component

Joomla! allows you to use Web Components (https://developer.mozilla.org/en-US/docs/Web/Web_Components) for your needs. In Joomla! web components are not loaded as regular script, but loaded via Web Component loader so that they are loaded asynchronously. Therefore, a web component asset item must have a flag "webcomponent" set to the boolean "true". In all other aspects, working with web components in AssetManager is the same as working with a "script" asset item.

Example json definition of some web components in joomla.asset.json:

...
{
  "name": "webcomponent.foobar",
  "type": "style",
  "uri": "com_example/foobar-custom-element.css",
  "webcomponent": true
},
{
  "name": "webcomponent.foobar",
  "type": "script",
  "uri": "com_example/foobar-custom-element.js",
  "webcomponent": true
}
...

Alternatively you can register them in PHP:

$wa->registerStyle('webcomponent.foobar', 'com_example/foobar-custom-element.css', ['webcomponent' => true])
    ->registerScript('webcomponent.foobar', 'com_example/foobar-custom-element.js', ['webcomponent' => true]);

Attach to document:

$wa->useStyle('webcomponent.foobar')
    ->useScript('webcomponent.foobar');

Note: It is preferred to prefix the asset name with "webcomponent." to make it easily to spot, and distinct it from regular scripts in a layout.

Methods to work with web component

All methods to work with a web component are the same as methods to work with script asset item.

Working with a presets

"Preset" is a special kind of asset item that hold a list of items that has to be enabled, in same way as direct call of useAsset() to each of item in the list. Preset can hold mixed types of assets (script, style, another preset, etc), the type should be provided after # symbol and follows after an asset name, example: foo#style, bar#script.

Example json defination of item in joomla.asset.json:

...
{
  "name": "foobar",
  "type": "preset",
  "uri": "",
  "dependencies": [
    "core#script",
    "foobar#style",
    "foobar#script",
  ]
}
...

Methods to work with preset

AssetManager offer next methods to work with preset items:

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();

// Attach all items from foobar preset to the document
$wa->usePreset('foobar');

// Disable all items from foobar preset from being attached
$wa->disablePreset('foobar');

// Register custom item without json defination
$wa->registerPreset('bar', '', [], [], ['core#script', 'bar#script']);
// And use it later
$wa->usePreset('bar');

// Register and attach a custom item in one run
$wa->registerAndUsePreset('bar','', [], [], ['core#script', 'bar#script']);

Avanzato: classe WebAssetItem personalizzata

La classe predefinita per tutti gli elementi WebAsset è Joomla\CMS\WebAsset\WebAssetItem.

Puoi anche usare una classe personalizzata, che deve implementare Joomla\CMS\WebAsset\WebAssetItemInterface o estendere Joomla\CMS\WebAsset\WebAssetItem.

Una classe personalizzata può consentire di eseguire azioni avanzate, ad esempio, incluso un file di script in base a una lingua attiva:

class MyComExampleAssetItem extends WebAssetItem
{
	public function getUri($resolvePath = true): string
	{
		$langTag = Factory::getApplication()->getLanguage()->getTag();
		// For script asset use ".js", for style we would use ".css"
		$path    = 'com_example/bar-' . $langTag . '.js';

		if ($resolvePath)
		{
			// For script asset use "script", for style we would use "stylesheet"
			$path = $this->resolvePath($path, 'script');
		}

		return $path;
	}
}

Inoltre, l'implementazione di "Joomla\CMS\WebAsset\WebAssetAttachBehaviorInterface'consente di aggiungere opzioni di script (che possono dipendere dall'ambiente) quando la risorsa è abilitata e allegata al Documento.

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

Important note: An asset item that implements WebAssetAttachBehaviorInterface should be enabled before onBeforeCompileHead event, otherwise 'onAttachCallback' will be ignored.

Definizione di una classe WebAssetItem personalizzata in joomla.asset.json

In joomla.asset.json è possibile definire quale classe utilizzare con AssetItem specifico. Per questo puoi usare 2 proprietà namespace e class. namespace può essere definito a livello di radice (quindi verrà utilizzato come spazio dei nomi predefinito per tutti gli elementi dell'asset in joomla.asset.json) o nel livello dell'oggetto. Per esempio:

{
  "$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json",
  "name": "com_example",
  "version": "4.0.0",
  "namespace": "Joomla\Component\Example\WebAsset",
  "assets": [
    {
      "name": "foo",
      "type": "script",
      "class": "FooAssetItem",
      "uri": "com_example/foo.js"
    },
    {
      "name": "bar",
      "type": "script",
      "namespace": "MyFooBar\Library\Example\WebAsset",
      "class": "BarAssetItem",
      "uri": "com_example/bar.js"
    }
  ]
}

Qui l'asset foo verrà associato alla classe Joomla\Component\Example\ WebAsset\FooAssetItem, e bar con la classe MyFooBar\Library\Example\WebAsset\BarAssetItem.

Nota: Se spazio dei nomi non è definito, verrà utilizzato per impostazione predefinita Joomla\CMS WebAsset. Quando namespace è definito ma vuoto, non verrà utilizzato nessuno spazio dei nomi, solo class. Esempio:

{
  "$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json",
  "name": "com_example",
  "assets": [
    {
      "name": "foo",
      "type": "script",
      "class": "FooAssetItem",
      "uri": "com_example/foo.js"
    },
    {
      "name": "bar",
      "type": "script",
      "namespace": "",
      "class": "BarAssetItem",
      "uri": "com_example/bar.js"
    }
  ]
}

Qui l'asset foo verrà associato alla classe Joomla\CMS\WebAsset\FooAssetItem, e bar con la classe BarAssetItem (senza spazio dei nomi) .