From a high-level perspective, the web client is a single-page application: it does not need to request a full page from the server each time the user performs an action. Instead, it only requests what it needs and then replaces/updates the current screen accordingly. Also, it manages the url to keep it in sync with the current state.
web/static/src folder contains all the
core/most of the low level features
fields/all field components
search/control panel, search bar, search panel, …
webclient/the web client specific code: navbar, user menu, action service, …
web/static/src is the root folder. Everything inside can simply be imported by using the
@web prefix. For example, here is how one can import the
memoize function located in
As mentioned above, the web client is an owl application. Here is a slightly simplified version of its template:
As we can see, it basically is a wrapper for a navbar, the current action and some additional components. The
ActionContainer is a higher order component that will display the current action controller (so, a client action, or a specific view in the case of actions of type
act_window). Managing actions is a huge part of its work: the action service keeps in memory a stack of all active actions (represented in the breadcrumbs), and coordinates each change.
Another interesting thing to note is the
MainComponentsContainer: it is simply a component that displays all components registered in the
main_components registry. This is how other parts of the system can extend the web client.
As an Owl application, the Odoo web client defines its own environment (components can access it using
this.env). Here is a description of what Odoo adds to the shared
required by Owl (contains all templates)
main bus, used to coordinate some generic events
all deployed services (should usually be accessed with the
string. If non empty, the web client is in debug mode
boolean. If true, the web client is currently in mobile mode (screen width <= 767px)
So, for example, to translate a string in a component (note: templates are automatically translated, so no specific action is required in that case), one can do this:
Most of the web client is built with a few types of abstractions: registries, services, components and hooks.
Registries are basically a simple key/value mapping that stores some specific kind of objects. They are an important part of the extensibility of the UI: once some object is registered, the rest of the web client can use it. For example, the field registry contains all field components (or widgets) that can be used in views.
Note that we import the main registry from
@web/core/registry then open the sub registry
Services are long lived pieces of code that provide a feature. They may be imported by components (with
useService) or by other services. Also, they can declare a set of dependencies. In that sense, services are basically a DI (dependency injection) system. For example, the
notification service provides a way to display a notification, or the
rpc service is the proper way to perform a request to the Odoo server.
The following example registers a simple service that displays a notification every 5 second:
Components and Hooks
Hooks are a way to factorize code, even if it depends on lifecycle. This is a composable/functional way to inject a feature in a component. They can be seen as a kind of mixin.
There are two different contexts in the Odoo web client: the user context and the action context (so, we should be careful when using the word context: it could mean a different thing depending on the situation).
The user context is a small object containing various informations related to the current user. It is available through the
It contains the following information:
the list of active company ids for the user
the user language code (such as “en_us”)
the user current timezone (for example “Europe/Brussels”)
In practice, the
orm service automatically adds the user context to each of its requests. This is why it is usually not necessary to import it directly in most cases.
The ir.actions.act_window and ir.actions.client support an optional
context field. This field is a
char that represents an object. Whenever the corresponding action is loaded in the web client, this context field will be evaluated as an object and given to the component that corresponds to the action.
It can be used in many different ways. For example, the views add the action context to every requests made to the server. Another important use is to activate some search filter by default (see example above).
In this example, the action with xml_id
addon_name.something will be loaded, and its context will be extended with the
default_period_id value. This is a very important usecase that lets developers combine actions together by providing some information to the next action.
The Odoo framework features a built-in small python interpreter. Its purpose is to evaluate small python expressions. This is important, because views in Odoo have modifiers written in python, but they need to be evaluated by the browser.
- evaluate(ast[, context])
- evaluateExpr(expr[, context])
! in prefix notation), or as string expressions. They don’t have to be normalized (the
& operator is implied if necessary). For example:
String expressions are more powerful than list expressions: they can contain python expressions and unevaluated values, that depends on some evaluation context. However, manipulating string expressions is more difficult.
Since domains are quite important in the web client, Odoo provides a
Here is the
Domain class description:
- class Domain([descr])
Domain class also provides 4 useful static methods to combine domains:
- static Domain.and(domains)
- static Domain.or(domains)
- static Domain.not(domain)
- static Domain.combine(domains, operator)
The web client environment object contains an event bus, named
bus. Its purpose is to allow various parts of the system to properly coordinate themselves, without coupling them. The
env.bus is an owl EventBus, that should be used for global events of interest.
Here is a list of the events that can be triggered on this bus:
a mode indicating what part of the ui has been updated (‘current’, ‘new’ or ‘fullscreen’)
the rendering of the action requested to the action manager is done
next rendering info
the action manager has finished computing the next interface
the menu service’s current app has changed
the url hash was changed
a rpc request has just started
a rpc request is completed
the web client has been mounted
the main view should focus itself
all internal caches should be cleared
list of functions
all views with uncommitted changes should clear them, and push a callback in the list
browser that provides access to many browser APIs, like
setTimeout. For example, here is how one could use the
It is mostly interesting for testing purposes: all code using the browser object can be tested easily by mocking the relevant functions for the duration of the test.
It contains the following content:
Odoo can sometimes operate in a special mode called the
debug mode. It is used for two main purposes:
display additional information/fields for some particular screens,
provide some additional tools to help developer debug the Odoo interface.
debug mode is described by a string. An empty string means that the
debug mode is not active. Otherwise, it is active. If the string contains
tests, then the corresponding specific sub modes are activated (see below). Both modes can be active at the same time, for example with the string
debug mode current value can be read in the environment:
There is another sub mode named
tests: if enabled, the server injects the bundle
web.assets_tests in the page. This bundle contains mostly test tours (tours whose purpose is to test a feature, not to show something interesting to users). The
tests mode is then useful to be able to run these tours.