documentation/content/developer/reference/frontend/assets.md

361 lines
12 KiB
Markdown
Raw Permalink Normal View History

2025-02-27 18:56:07 +07:00
(reference-assets)=
2025-02-27 18:56:07 +07:00
# Assets
Managing assets in Odoo is not as straightforward as it is in some other apps.
One of the reasons is that we have a variety of situations where some, but not all
of the assets are required. For example, the needs of the web client, the point of
sale app, the website or even the mobile application are different. Also, some
assets may be large, but are seldom needed: in that case we may want them
2025-02-27 18:56:07 +07:00
to be {ref}`loaded lazily (on demand) <frontend/assets/lazy_loading>`.
2025-02-27 18:56:07 +07:00
## Asset types
There are three different asset types: code (`js` files), style (`css` or `scss`
files) and templates (`xml` files).
Code
2025-02-27 18:56:07 +07:00
: Odoo supports {ref}`three different kinds of javascript files<frontend/js_modules>`.
All these files are then processed (native JS modules are transformed into odoo
modules), then minified (if not in `debug=assets` {ref}`mode <frontend/framework/assets_debug_mode>`)
and concatenated. The result is then saved as a file attachment. These file
attachments are usually loaded via a `<script>` tag in the `<head>` part of
the page (as a static file).
Style
2025-02-27 18:56:07 +07:00
: Styling can be done with either `css` or [scss](https://sass-lang.com/). Like
the javascript files, these files are processed (`scss` files are converted into
`css`), then minified (again, if not in `debug=assets` {ref}`mode <frontend/framework/assets_debug_mode>`)
and concatenated. The result is then saved as a file attachment. They are
then usually loaded via a `<link>` tag in the `<head>` part of the page (as
a static file).
Template
2025-02-27 18:56:07 +07:00
: Templates (static `xml` files) are handled in a different way: they are simply
read from the file system whenever they are needed, and concatenated.
Whenever the browser loads odoo, it calls the `/web/webclient/qweb/` controller
to fetch the {ref}`templates <reference/qweb>`.
It is useful to know that in most cases, a browser only performs a request the
first time it loads a page. This is because each of these assets are
associated with a checksum, which is injected into the page source. The checksum
is then added to the url, which means that it is possible to safely set the cache
headers to a long period.
2025-02-27 18:56:07 +07:00
(reference-assets-bundle)=
2025-02-27 18:56:07 +07:00
## Bundles
Odoo assets are grouped by *bundles*. Each bundle (a *list of file paths*
of specific types: `xml`, `js`, `css` or `scss`) is listed in the
2025-02-27 18:56:07 +07:00
{ref}`module manifest <reference/module/manifest>`. Files can be declared using
[glob](<https://en.wikipedia.org/wiki/Glob_(programming)>) syntax, meaning that
you can declare several asset files using a single line.
2025-02-27 18:56:07 +07:00
The bundles are defined in each module's {file}`__manifest__.py`,
with a dedicated `assets` key which contains a dictionary. The dictionary maps
bundle names (keys) to the list of files they contain (values). It looks
like this:
2025-02-27 18:56:07 +07:00
```python
'assets': {
'web.assets_backend': [
'web/static/src/xml/**/*',
],
'web.assets_common': [
'web/static/lib/bootstrap/**/*',
'web/static/src/js/boot.js',
'web/static/src/js/webclient.js',
'web/static/src/xml/webclient.xml',
],
'web.qunit_suite_tests': [
'web/static/src/js/webclient_tests.js',
],
},
```
Here is a list of some important bundles that most odoo developers will need to
know:
- `web.assets_common`: this bundle contains most assets which are common to the
web client, the website and also the point of sale. This is supposed to contain
lower level building blocks for the odoo framework. Note that it contains the
2025-02-27 18:56:07 +07:00
{file}`boot.js` file, which defines the odoo module system.
- `web.assets_backend`: this bundle contains the code specific to the web client
(notably the web client/action manager/views/static XML templates)
- `web.assets_frontend`: this bundle is about all that is specific to the public
website: ecommerce, portal, forum, blog, ...
- `web.qunit_suite_tests`: all javascript qunit testing code (tests, helpers, mocks)
- `web.qunit_mobile_suite_tests`: mobile specific qunit testing code
2025-02-27 18:56:07 +07:00
### Operations
Typically, handling assets is simple: you just need to add some new files
to a frequently used bundle like `assets_common` or `assets_backend`. But there are other operations
available to cover some more specific use cases.
Note that all directives targeting a certain asset file (i.e. `before`, `after`,
`replace` and `remove`) need that file to be declared beforehand, either
2025-02-27 18:56:07 +07:00
in manifests higher up in the hierarchy or in `ir.asset` records with a lower
sequence.
2025-02-27 18:56:07 +07:00
#### `append`
This operation adds one or multiple file(s). Since it is the most common
operation, it can be done by simply using the file name:
2025-02-27 18:56:07 +07:00
```python
'web.assets_common': [
'my_addon/static/src/js/**/*',
],
```
By default, adding a simple string to a bundle will append the files matching the
glob pattern at the end of the bundle. Obviously, the pattern may also be directly
a single file path.
2025-02-27 18:56:07 +07:00
#### `prepend`
Add one or multiple file(s) at the beginning of the bundle.
Useful when you need to put a certain file before the others in a bundle (for
example with css files). The `prepend` operation is invoked with the following
syntax: `('prepend', <path>)`.
2025-02-27 18:56:07 +07:00
```python
'web.assets_common': [
('prepend', 'my_addon/static/src/css/bootstrap_overridden.scss'),
],
```
2025-02-27 18:56:07 +07:00
#### `before`
Add one or multiple file(s) before a specific file.
Prepending a file at the beginning of a bundle might not be precise enough. The
`before` directive can be used to add the given file(s) right *before* the target
file. It is declared by replacing the normal path with a 3-element tuple
`('before', <target>, <path>)`.
2025-02-27 18:56:07 +07:00
```python
'web.assets_common': [
('before', 'web/static/src/css/bootstrap_overridden.scss', 'my_addon/static/src/css/bootstrap_overridden.scss'),
],
```
2025-02-27 18:56:07 +07:00
#### `after`
Add one or multiple file(s) after a specific file.
Same as `before`, with the matching file(s) appended right *after* the target file.
It is declared by replacing the normal path with a 3-element tuple
`('after', <target>, <path>)`.
2025-02-27 18:56:07 +07:00
```python
'web.assets_common': [
('after', 'web/static/src/css/list_view.scss', 'my_addon/static/src/css/list_view.scss'),
],
```
2025-02-27 18:56:07 +07:00
#### `include`
Use nested bundles.
The `include` directive is a way to use a bundle in other bundles to minimize
the size of your manifest. In Odoo we use sub bundles (prefixed with an underscore
by convention) to batch files used in multiple other bundles. You can then
specify the sub bundle as a pair `('include', <bundle>)` like this:
2025-02-27 18:56:07 +07:00
```python
'web.assets_common': [
('include', 'web._primary_variables'),
],
```
2025-02-27 18:56:07 +07:00
#### `remove`
Remove one or multiple file(s).
In some cases, you may want to remove one or multiple files from a bundle. This
can be done using the `remove` directive by specifying a pair
`('remove', <target>)`:
2025-02-27 18:56:07 +07:00
```python
'web.assets_common': [
('remove', 'web/static/src/js/boot.js'),
],
```
2025-02-27 18:56:07 +07:00
#### `replace`
Replace an asset file with one or multiple file(s).
Let us say that an asset needs not only to be removed, but you also want to insert
your new version of that asset at the same exact position. This can be done with
the `replace` directive, using a 3-element tuple `('replace', <target>, <path>)`:
2025-02-27 18:56:07 +07:00
```python
'web.assets_common': [
('replace', 'web/static/src/js/boot.js', 'my_addon/static/src/js/boot.js'),
],
```
2025-02-27 18:56:07 +07:00
### Loading order
The order in which assets are loaded is sometimes critical and must be deterministic,
mostly for stylesheets priorities and setup scripts. Assets in Odoo are processed
as follows:
2025-02-27 18:56:07 +07:00
1. When an asset bundle is called (e.g. `t-call-assets="web.assets_common"`), an empty
list of assets is generated
2025-02-27 18:56:07 +07:00
2. All records of type `ir.asset` matching the bundle are fetched and sorted
by sequence number. Then all records with a sequence strictly less than 16 are
processed and applied to the current list of assets.
2025-02-27 18:56:07 +07:00
3. All modules declaring assets for said bundle in their manifest apply their
assets operations to this list. This is done following the order of modules dependencies
(e.g. `web` assets is processed before `website`). If a directive tries to add
a file already present in the list, nothing is done for that file. In other word,
only the first occurrence of a file is kept in the list.
2025-02-27 18:56:07 +07:00
4. The remaining `ir.asset` records (those with a sequence greater than or equal
to 16) are then processed and applied as well.
Assets declared in the manifest may need to be loaded in a particular order, for
2025-02-27 18:56:07 +07:00
example {file}`jquery.js` must be loaded before all other jquery scripts when loading the
lib folder. One solution would be to create an {ref}`ir.asset <frontend/assets/ir_asset>`
record with a lower sequence or a 'prepend' directive, but there is another simpler
way to do so.
Since the unicity of each file path in the list of assets is guaranteed, you can
mention any specific file before a glob that includes it. That file will thus appear
in the list before all the others included in the glob.
2025-02-27 18:56:07 +07:00
```python
'web.assets_common': [
'my_addon/static/lib/jquery/jquery.js',
'my_addon/static/lib/jquery/**/*',
],
```
2025-02-27 18:56:07 +07:00
:::{note}
A module *b* removing/replacing the assets declared in a module *a* will have
to depend on it. Trying to operate on assets that have yet to be declared will
result in an error.
:::
2025-02-27 18:56:07 +07:00
(frontend-assets-lazy-loading)=
2025-02-27 18:56:07 +07:00
## Lazy loading
It is sometimes useful to load files and/or asset bundles dynamically, for
example to only load a library once it is needed. To do that, the Odoo framework
2025-02-27 18:56:07 +07:00
provides a few helper functions, located in {file}`@web/core/assets`.
2025-02-27 18:56:07 +07:00
```javascript
await loadAssets({
jsLibs: ["/web/static/lib/stacktracejs/stacktrace.js"],
});
```
2025-02-27 18:56:07 +07:00
```{eval-rst}
.. js:function:: loadAssets(assets)
:param Object assets: a description of various assets that should be loaded
:returns: Promise<void>
Load the assets described by the `assets` parameter. It is an object that
may contain the following keys:
.. list-table::
:widths: 20 20 60
:header-rows: 1
* - Key
- Type
- Description
* - `jsLibs`
- `string[]`
- a list of urls of javascript files
* - `cssLibs`
- `string[]`
- a list of urls of css files
2025-02-27 18:56:07 +07:00
```
2025-02-27 18:56:07 +07:00
```{eval-rst}
.. js:function:: useAssets(assets)
:param Object assets: a description of various assets that should be loaded
This hook is useful when components need to load some assets in their
`onWillStart` method. It internally calls `loadAssets`.
2025-02-27 18:56:07 +07:00
```
2025-02-27 18:56:07 +07:00
(frontend-assets-ir-asset)=
2025-02-27 18:56:07 +07:00
## The asset model (`ir.asset`)
In most cases the assets declared in the manifest will largely suffice. Yet for
more flexibility, the framework also supports dynamic assets declared in the
database.
This is done by creating `ir.asset` records. Those will be processed as if they
were found in a module manifest, and they give the same expressive power as their
manifest counterparts.
2025-02-27 18:56:07 +07:00
```{eval-rst}
.. autoclass:: odoo.addons.base.models.ir_asset.IrAsset
2025-02-27 18:56:07 +07:00
```
2025-02-27 18:56:07 +07:00
```{eval-rst}
2025-02-28 00:37:20 +07:00
.. container:: o-definition-list
2025-02-27 18:56:07 +07:00
```
`name`
2025-02-27 18:56:07 +07:00
: Name of the asset record (for identification purpose).
`bundle`
2025-02-27 18:56:07 +07:00
: Bundle in which the asset will be applied.
`directive` (default= `append`)
2025-02-27 18:56:07 +07:00
: This field determines how the `path` (and `target` if needed) will be interpreted.
Here is the list of available directives along with their required arguments:
- **append**: `path`
- **prepend**: `path`
- **before**: `target`, `path`
- **after**: `target`, `path`
- **include**: `path` (interpreted as a **bundle name**)
- **remove**: `path` (interpreted as a **target asset** to remove)
- **replace**: `target`, `path`
`path`
2025-02-27 18:56:07 +07:00
: A string defining one of the following:
- a **relative path** to an asset file in the addons file system;
- a **glob pattern** to a set of asset files in the addons file system;
- a **URL** to an attachment or external asset file;
- a **bundle name**, when using the `include` directive.
`target`
2025-02-27 18:56:07 +07:00
: Target file to specify a position in the bundle. Can only be used with the
directives `replace`, `before` and `after`.
`active` (default= `True`)
2025-02-27 18:56:07 +07:00
: Whether the record is active
`sequence` (default= `16`)
2025-02-27 18:56:07 +07:00
: Loading order of the asset records (ascending). A sequence lower than 16 means
that the asset will be processed *before* the ones declared in the manifest.