OWL
navigation
Nav Bar
Odoo 19 OWL component — Nav Bar (webclient)
Live preview
Interactive
Source excerpt
web/static/src/webclient/navbar/navbar.js
import { Dropdown } from "@web/core/dropdown/dropdown";
import { DropdownItem } from "@web/core/dropdown/dropdown_item";
import { DropdownGroup } from "@web/core/dropdown/dropdown_group";
import { Transition } from "@web/core/transition";
import { useService } from "@web/core/utils/hooks";
import { registry } from "@web/core/registry";
import { debounce } from "@web/core/utils/timing";
import { ErrorHandler } from "@web/core/utils/components";
import {
Component,
onWillDestroy,
useExternalListener,
useEffect,
useRef,
useState,
onWillUnmount,
} from "@odoo/owl";
const systrayRegistry = registry.category("systray");
const getBoundingClientRect = Element.prototype.getBoundingClientRect;
const SWIPE_ACTIVATION_THRESHOLD = 100;
export class MenuDropdown extends Dropdown {}
export class NavBar extends Component {
static template = "web.NavBar";
static components = {
Dropdown,
DropdownItem,
DropdownGroup,
MenuDropdown,
ErrorHandler,
Transition,
};
static props = {};
setup() {
this.currentAppSectionsExtra = [];
this.actionService = useService("action");
this.menuService = useService("menu");
this.pwa = useService("pwa");
this.root = useRef("root");
this.appSubMenus = useRef("appSubMenus");
const debouncedAdapt = debounce(this.adapt.bind(this), 250);
onWillDestroy(() => debouncedAdapt.cancel());
useExternalListener(window, "resize", debouncedAdapt);
let adaptCounter = 0;
const renderAndAdapt = () => {
adaptCounter++;
this.render();
};
systrayRegistry.addEventListener("UPDATE", renderAndAdapt);
this.env.bus.addEventListener("MENUS:APP-CHANGED", renderAndAdapt);
onWillUnmount(() => {
systrayRegistry.removeEventListener("UPDATE", renderAndAdapt);
this.env.bus.removeEventListener("MENUS:APP-CHANGED", renderAndAdapt);
});
// We don't want to adapt every time we are patched
// rather, we adapt only when menus or systrays have changed.
useEffect(
() => {
this.adapt();
},
() => [adaptCounter]
);
this.state = useState({
isAllAppsMenuOpened: false,
isAppMenuSidebarOpened: false,
});
this.ui = useState(useService("ui"));
}
handleItemError(error, item) {
// remove the faulty component
item.isDisplayed = () => false;
Promise.resolve().then(() => {
throw error;
});
}
get currentApp() {
return this.menuService.getCurrentApp();
}
get currentAppSections() {
return (
(this.currentApp && this.menuService.getMenuAsTree(this.currentApp.id).childrenTree) ||
[]
);
}
// This dummy setter is only here to prevent conflicts between the
// Enterprise NavBar extension and the Website NavBar patch.
set currentAppSections(_) {}
get isScopedApp() {
return this.pwa.isScopedApp;
}
get systrayItems() {
return systrayRegistry
.getEntries()
.map(([key, value]) => ({ key, ...value }))
.filter((item) => ("isDisplayed" in item ? item.isDisplayed(this.env) : true))
.reverse();
}
// This dummy setter is only here to prevent conflicts between the
// Enterprise NavBar extension and the Website NavBar patch.
set systrayItems(_) {}
/**
* Adapt will check the available width for the app sections to get displayed.
Registry / API
- Registry name
NavBar- Category
—- Module
web- Slug
nav-bar- Nav group
navigation