OWL
data_display
Lazy Component
Odoo 19 OWL component — Lazy Component (core)
Live preview
Interactive
Source excerpt
web/static/src/core/assets.js
import { Component, onWillStart, whenReady, xml } from "@odoo/owl";
import { session } from "@web/session";
import { registry } from "./registry";
/**
* @typedef {{
* cssLibs: string[];
* jsLibs: string[];
* }} BundleFileNames
*/
export const globalBundleCache = new Map();
export const assetCacheByDocument = new WeakMap();
function getGlobalBundleCache() {
return globalBundleCache;
}
function getAssetCache(targetDoc) {
if (!assetCacheByDocument.has(targetDoc)) {
assetCacheByDocument.set(targetDoc, new Map());
}
return assetCacheByDocument.get(targetDoc);
}
export function computeBundleCacheMap(targetDoc) {
const cacheMap = getGlobalBundleCache();
for (const script of targetDoc.head.querySelectorAll("script[src]")) {
cacheMap.set(script.getAttribute("src"), Promise.resolve());
}
for (const link of targetDoc.head.querySelectorAll("link[rel=stylesheet][href]")) {
cacheMap.set(link.getAttribute("href"), Promise.resolve());
}
}
whenReady(() => computeBundleCacheMap(document));
/**
* @param {HTMLLinkElement | HTMLScriptElement} el
* @param {(event: Event) => any} onLoad
* @param {(error: Error) => any} onError
*/
const onLoadAndError = (el, onLoad, onError) => {
const onLoadListener = (event) => {
removeListeners();
onLoad(event);
};
const onErrorListener = (error) => {
removeListeners();
onError(error);
};
const removeListeners = () => {
el.removeEventListener("load", onLoadListener);
el.removeEventListener("error", onErrorListener);
};
el.addEventListener("load", onLoadListener);
el.addEventListener("error", onErrorListener);
window.addEventListener("pagehide", () => {
removeListeners();
});
};
/** @type {typeof assets["getBundle"]} */
export function getBundle() {
return assets.getBundle(...arguments);
}
/** @type {typeof assets["loadBundle"]} */
export function loadBundle() {
return assets.loadBundle(...arguments);
}
/** @type {typeof assets["loadJS"]} */
export function loadJS() {
return assets.loadJS(...arguments);
}
/** @type {typeof assets["loadCSS"]} */
export function loadCSS() {
return assets.loadCSS(...arguments);
}
export class AssetsLoadingError extends Error {}
/**
* Utility component that loads an asset bundle before instanciating a component
*/
export class LazyComponent extends Component {
static template = xml`<t t-component="Component" t-props="componentProps"/>`;
static props = {
Component: String,
bundle: String,
props: { type: [Object, Function], optional: true },
};
setup() {
onWillStart(async () => {
await loadBundle(this.props.bundle);
this.Component = registry.category("lazy_components").get(this.props.Component);
});
}
get componentProps() {
return typeof this.props.props === "function" ? this.props.props() : this.props.props;
}
}
/**
* This export is done only in order to modify the behavior of the exported
* functions. This is done in order to be able to make a test environment.
* Modules should only use the methods exported below.
*/
export const assets = {
retries: {
count: 3,
delay: 5000,
extraDelay: 2500,
Registry / API
- Registry name
LazyComponent- Category
—- Module
web- Slug
lazy-component- Nav group
data_display