462 lines
15 KiB
ReStructuredText
462 lines
15 KiB
ReStructuredText
==================
|
|
Framework Overview
|
|
==================
|
|
|
|
Introduction
|
|
============
|
|
|
|
The Odoo Javascript framework is a set of features/building blocks provided by
|
|
the ``web/`` addon to help build odoo applications running in the browser. At
|
|
the same time, the Odoo Javascript framework is a single page application,
|
|
usually known as the *web client* (available at the url ``/web``).
|
|
|
|
The web client started as an application made with a custom class and widget
|
|
system, but it is now transitioning to using native javascript classes instead,
|
|
and Owl as a component system. This explains why both systems are currently in
|
|
use in the codebase.
|
|
|
|
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.
|
|
|
|
The javascript framework (all or some parts) is also used in other situations,
|
|
such as the Odoo website or the point of sale. This reference is mostly focused
|
|
on the web client.
|
|
|
|
.. note::
|
|
|
|
It is common in the Odoo ecosystem to see the words *frontend* and *backend*
|
|
as synonyms for the odoo website (public) and the web client, respectively.
|
|
This terminology is not to be confused with the more common use of
|
|
browser-code (frontend) and server (backend).
|
|
|
|
.. note::
|
|
|
|
In this documentation, the word *component* always refers to new Owl
|
|
components, and *widget* refers to old Odoo widgets.
|
|
|
|
.. note::
|
|
|
|
All new development should be done in Owl, if possible!
|
|
|
|
Code structure
|
|
==============
|
|
|
|
The ``web/static/src`` folder contains all the ``web/`` javascript (and css and
|
|
templates) codebase. Here is a list of the most important folders:
|
|
|
|
- ``core/`` most of the low level features
|
|
- ``fields/`` all field components
|
|
- ``views/`` all javascript views components (``form``, ``list``, ...)
|
|
- ``search/`` control panel, search bar, search panel, ...
|
|
- ``webclient/`` the web client specific code: navbar, user menu, action service, ...
|
|
|
|
The ``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 ``web/static/src/core/utils/functions``:
|
|
|
|
.. code-block:: javascript
|
|
|
|
import { memoize } from "@web/core/utils/functions";
|
|
|
|
WebClient Architecture
|
|
======================
|
|
|
|
As mentioned above, the web client is an owl application. Here is a slightly
|
|
simplified version of its template:
|
|
|
|
.. code-block:: xml
|
|
|
|
<t t-name="web.WebClient" owl="1">
|
|
<body class="o_web_client">
|
|
<NavBar/>
|
|
<ActionContainer/>
|
|
<MainComponentsContainer/>
|
|
</body>
|
|
</t>
|
|
|
|
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.
|
|
|
|
.. _javascript/environment:
|
|
|
|
Environment
|
|
===========
|
|
|
|
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 ``env`` object:
|
|
|
|
.. list-table::
|
|
:widths: 30 70
|
|
:header-rows: 1
|
|
|
|
* - Key
|
|
- Value
|
|
* - `qweb`
|
|
- required by Owl (contains all templates)
|
|
* - `bus`
|
|
- :ref:`main bus <javascript/bus>`, used to coordinate some generic events
|
|
* - `services`
|
|
- all deployed services (should usually be accessed with the `useService` hook)
|
|
* - `debug`
|
|
- string. If non empty, the web client is in :ref:`debug mode<javascript/debug_mode>`
|
|
* - `_t`
|
|
- translation function
|
|
* - `isSmall`
|
|
- boolean. If true, the web client is currently in mobile mode
|
|
|
|
|
|
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:
|
|
|
|
|
|
.. code-block:: javascript
|
|
|
|
const someString = this.env._t('some text');
|
|
|
|
|
|
Building Blocks
|
|
===============
|
|
|
|
Most of the web client is built with a few types of abstractions: registries,
|
|
services, components and hooks.
|
|
|
|
Registries
|
|
----------
|
|
|
|
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.
|
|
|
|
.. code-block:: javascript
|
|
|
|
import { registry } from "./core/registry";
|
|
|
|
class MyFieldChar extends owl.Component {
|
|
// some code
|
|
}
|
|
|
|
registry.category("fields").add("my_field_char", MyFieldChar);
|
|
|
|
Note that we import the main registry from ``@web/core/registry`` then open the
|
|
sub registry ``fields``.
|
|
|
|
Services
|
|
--------
|
|
|
|
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:
|
|
|
|
.. code-block:: javascript
|
|
|
|
import { registry } from "./core/registry";
|
|
|
|
const myService = {
|
|
dependencies: ["notification"],
|
|
start(env, { notification }) {
|
|
let counter = 1;
|
|
setInterval(() => {
|
|
notification.add(`Tick Tock ${counter++}`);
|
|
}, 5000);
|
|
}
|
|
};
|
|
|
|
serviceRegistry.add("myService", myService);
|
|
|
|
Components and Hooks
|
|
--------------------
|
|
|
|
Components and hooks are ideas coming from the
|
|
`Owl component system <https://github.com/odoo/owl/blob/master/doc/readme.md>`_.
|
|
Odoo components are simply owl components that are part of the web client.
|
|
|
|
`Hooks <https://github.com/odoo/owl/blob/master/doc/reference/hooks.md>`_ 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.
|
|
|
|
.. code-block:: javascript
|
|
|
|
function useCurrentTime() {
|
|
const state = useState({ now: new Date() });
|
|
const update = () => state.now = new Date();
|
|
let timer;
|
|
onWillStart(() => timer = setInterval(update, 1000));
|
|
onWillUnmount(() => clearInterval(timer));
|
|
return state;
|
|
}
|
|
|
|
Context
|
|
=======
|
|
|
|
An important concept in the Odoo javascript is the *context*: it provides a way
|
|
for code to give more context to a function call or a rpc, so other parts of the
|
|
system can properly react to that information. In some way, it is like a bag of
|
|
information that is propagated everywhere. It is useful in some situations, such
|
|
as letting the Odoo server know that a model rpc comes from a specific form view,
|
|
or activating/disabling some features in a component.
|
|
|
|
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).
|
|
|
|
.. note::
|
|
The `context` object may be useful in many cases, but one should be careful
|
|
not to overuse it! Many problems can be solved in a standard way without
|
|
modifying the context.
|
|
|
|
.. _javascript/user-context:
|
|
|
|
User Context
|
|
------------
|
|
|
|
The *user context* is a small object containing various informations related to
|
|
the current user. It is available through the `user` service:
|
|
|
|
.. code-block:: javascript
|
|
|
|
class MyComponent extends Component {
|
|
setup() {
|
|
const user = useService("user");
|
|
console.log(user.context);
|
|
}
|
|
}
|
|
|
|
It contains the following information:
|
|
|
|
|
|
.. list-table::
|
|
:widths: 20 20 60
|
|
:header-rows: 1
|
|
|
|
* - Name
|
|
- Type
|
|
- Description
|
|
* - `allowed_company_ids`
|
|
- `number[]`
|
|
- the list of active company ids for the user
|
|
* - `lang`
|
|
- `string`
|
|
- the user language code (such as "en_us")
|
|
* - `tz`
|
|
- `string`
|
|
- 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.
|
|
|
|
.. note::
|
|
The first element of the `allowed_company_ids` is the main company of the user.
|
|
|
|
Action Context
|
|
--------------
|
|
|
|
The :ref:`ir.actions.act_window<reference/actions/window>` and
|
|
:ref:`ir.actions.client<reference/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.
|
|
|
|
.. code-block:: xml
|
|
|
|
<field name="context">{'search_default_customer': 1}</field>
|
|
|
|
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).
|
|
|
|
Sometimes, when we execute new actions manually (so, programmatically, in javascript),
|
|
it is useful to be able to extend the action context. This can be done with the
|
|
`additional_context` argument.
|
|
|
|
.. code-block:: javascript
|
|
|
|
// in setup
|
|
let actionService = useService("action");
|
|
|
|
// in some event handler
|
|
actionService.doAction("addon_name.something", {
|
|
additional_context:{
|
|
default_period_id: defaultPeriodId
|
|
}
|
|
});
|
|
|
|
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.
|
|
|
|
.. _javascript/bus:
|
|
|
|
Bus
|
|
===
|
|
|
|
The web client :ref:`environment <javascript/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 <https://github.com/odoo/owl/blob/master/doc/reference/event_bus.md>`_,
|
|
that should be used for global events of interest.
|
|
|
|
|
|
.. code-block:: javascript
|
|
|
|
// for example, in some service code:
|
|
env.bus.on("WEB_CLIENT_READY", null, doSomething);
|
|
|
|
Here is a list of the events that can be triggered on this bus:
|
|
|
|
.. list-table::
|
|
:header-rows: 1
|
|
|
|
* - Message
|
|
- Payload
|
|
- Trigger
|
|
* - ``ACTION_MANAGER:UI-UPDATED``
|
|
- 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
|
|
* - ``ACTION_MANAGER:UPDATE``
|
|
- next rendering info
|
|
- the action manager has finished computing the next interface
|
|
* - ``MENUS:APP-CHANGED``
|
|
- none
|
|
- the menu service's current app has changed
|
|
* - ``ROUTE_CHANGE``
|
|
- none
|
|
- the url hash was changed
|
|
* - ``RPC:REQUEST``
|
|
- rpc id
|
|
- a rpc request has just started
|
|
* - ``RPC:RESPONSE``
|
|
- rpc id
|
|
- a rpc request is completed
|
|
* - ``WEB_CLIENT_READY``
|
|
- none
|
|
- the web client has been mounted
|
|
* - ``FOCUS-VIEW``
|
|
- none
|
|
- the main view should focus itself
|
|
* - ``CLEAR-CACHES``
|
|
- none
|
|
- all internal caches should be cleared
|
|
* - ``CLEAR-UNCOMMITTED-CHANGES``
|
|
- list of functions
|
|
- all views with uncommitted changes should clear them, and push a callback in the list
|
|
|
|
|
|
Browser Object
|
|
==============
|
|
|
|
The javascript framework also provides a special object ``browser`` that
|
|
provides access to all browser APIs, like ``location``, ``localStorage``
|
|
or ``setTimeout``. For example, here is how one could use the
|
|
``browser.setTimeout`` function:
|
|
|
|
.. code-block:: javascript
|
|
|
|
import { browser } from "@web/core/browser/browser";
|
|
|
|
// somewhere in code
|
|
browser.setTimeout(someFunction, 1000);
|
|
|
|
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.
|
|
|
|
.. _javascript/debug_mode:
|
|
|
|
Debug mode
|
|
==========
|
|
|
|
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.
|
|
|
|
The `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 `assets` or
|
|
`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 `assets,tests`.
|
|
|
|
The `debug` mode current value can be read in the :ref:`environment<javascript/environment>`:
|
|
`env.debug`.
|
|
|
|
.. tip::
|
|
|
|
To show menus, fields or view elements only in debug mode, you should target
|
|
the group `base.group_no_one`:
|
|
|
|
.. code-block:: xml
|
|
|
|
<field name="fname" groups="base.group_no_one"/>
|
|
|
|
Toggling debug mode
|
|
-------------------
|
|
|
|
The most common way to activate the debug mode is to edit the url: just add
|
|
`?debug=1` or `?debug=assets` before the `#` sign, then reload the page. This
|
|
will cause the server to mark the session as being in debug mode.
|
|
|
|
To deactivate the debug mode, one can then simply add `?debug=0` to explicitely
|
|
notify the server that we no longer want it.
|
|
|
|
An alternative way to toggle the debug mode is to use the command palette. First
|
|
step is to open it (ctrl+k), then type `debug`: a command will show up to
|
|
activate the debug mode.
|
|
|
|
Debug menu
|
|
----------
|
|
|
|
Once the debug mode is activated, an additional sub menu, called the `debug menu`,
|
|
is available in the systray (in the top bar), or in the title bar of some
|
|
dialogs. This menu contains additional tools that are useful to understand
|
|
or edit technical data, such as the views or the actions. It contains
|
|
some useful menu items such as:
|
|
|
|
- edit action
|
|
- manage filters
|
|
- edit the current view
|
|
- see the `fields view get`
|
|
- and much more.
|
|
|
|
Assets mode
|
|
-----------
|
|
|
|
The `debug=assets` sub mode is useful to debug javascript code: once activated,
|
|
the :ref:`assets<reference/assets>` bundles are no longer minified, and source-maps
|
|
are generated as well. This makes it useful to debug all kind of javascript code.
|
|
|
|
Tests mode
|
|
----------
|
|
|
|
There is a specific 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.
|
|
|
|
.. seealso::
|
|
- `Owl Repository <https://github.com/odoo/owl>`_ |