summaryrefslogtreecommitdiff
path: root/doc/reference/javascript_reference.rst
diff options
context:
space:
mode:
Diffstat (limited to 'doc/reference/javascript_reference.rst')
-rw-r--r--doc/reference/javascript_reference.rst463
1 files changed, 427 insertions, 36 deletions
diff --git a/doc/reference/javascript_reference.rst b/doc/reference/javascript_reference.rst
index e6ca7a4486b..7f0e606e28e 100644
--- a/doc/reference/javascript_reference.rst
+++ b/doc/reference/javascript_reference.rst
@@ -49,7 +49,7 @@ Single Page Application
-----------------------
In short, the *webClient*, instance of *WebClient* is the root component of the
-whole user interface. Its responsability is to orchestrate all various
+whole user interface. Its responsibility is to orchestrate all various
subcomponents, and to provide services, such as rpcs, local storage and more.
In runtime, the web client is a single page application. It does not need to
@@ -95,7 +95,7 @@ assets may be large, but are seldom needed. In that case, we sometimes want the
to be loaded lazily.
The main idea is that we define a set of *bundles* in xml. A bundle is here defined as
-a collection of files (javascript, css, less). In Odoo, the most important
+a collection of files (javascript, css, scss). In Odoo, the most important
bundles are defined in the file *addons/web/views/webclient_templates.xml*. It looks
like this:
@@ -119,11 +119,11 @@ directive:
Here is what happens when a template is rendered by the server with these directives:
-- all the *less* files described in the bundle are compiled into css files. A file
- named *file.less* will be compiled in a file named *file.less.css*.
+- all the *scss* files described in the bundle are compiled into css files. A file
+ named *file.scss* will be compiled in a file named *file.scss.css*.
- if we are in *debug=assets* mode,
- - the *t-call-assets* directive with the *t-js* attribute set to false will
+ - the *t-call-assets* directive with the *t-js* attribute set to false will
be replaced by a list of stylesheet tags pointing to the css files
- the *t-call-assets* directive with the *t-css* attribute set to false will
be replaced by a list of script tags pointing to the js files
@@ -173,7 +173,7 @@ to add a file from that addon. In that case, it should be done in three steps:
<template id="assets_backend" name="helpdesk assets" inherit_id="web.assets_backend">
<xpath expr="//script[last()]" position="after">
- <link rel="stylesheet" href="/helpdesk/static/src/less/helpdesk.less"/>
+ <link rel="stylesheet" type="text/scss" href="/helpdesk/static/src/scss/helpdesk.scss"/>
<script type="text/javascript" src="/helpdesk/static/src/js/helpdesk_dashboard.js"></script>
</xpath>
</template>
@@ -259,7 +259,7 @@ As an example, it may look like this:
return B;
});
-An alternative way to define a module is to give explicitely a list of dependencies
+An alternative way to define a module is to give explicitly a list of dependencies
in the second argument.
.. code-block:: javascript
@@ -296,7 +296,7 @@ The *odoo.define* method is given three arguments:
- *dependencies*: the second argument is optional. If given, it should be a list
of strings, each corresponding to a javascript module. This describes the
dependencies that are required to be loaded before the module is executed. If
- the dependencies are not explicitely given here, then the module system will
+ the dependencies are not explicitly given here, then the module system will
extract them from the function by calling toString on it, then using a regexp
to find all *require* statements.
@@ -308,12 +308,12 @@ The *odoo.define* method is given three arguments:
If an error happens, it will be logged (in debug mode) in the console:
* ``Missing dependencies``:
- These modules do not appear in the page. It is possible that the JavaScript
+ These modules do not appear in the page. It is possible that the JavaScript
file is not in the page or that the module name is wrong
* ``Failed modules``:
A javascript error is detected
* ``Rejected modules``:
- The module returns a rejected deferred. It (and its dependent modules) is not
+ The module returns a rejected deferred. It (and its dependent modules) is not
loaded.
* ``Rejected linked modules``:
Modules who depend on a rejected module
@@ -366,7 +366,7 @@ Best practices
Class System
============
-Odoo was developped before ECMAScript 6 classes were available. In Ecmascript 5,
+Odoo was developed before ECMAScript 6 classes were available. In Ecmascript 5,
the standard way to define a class is to define a function and to add methods
on its prototype object. This is fine, but it is slightly complex when we want
to use inheritance, mixins.
@@ -566,7 +566,7 @@ rendering takes place, then *start* and finally *destroy*.
hook that should return a deferred. The JS framework will wait for this deferred
to complete before moving on to the rendering step. Note that at this point,
the widget does not have a DOM root element. The *willStart* hook is mostly
- useful to perfom some asynchronous work, such as fetching data from the server
+ useful to perform some asynchronous work, such as fetching data from the server
.. function:: [Rendering]
@@ -616,33 +616,33 @@ Widget API
generated DOM root with the following attributes:
- .. attribute:: Widget.id
+.. attribute:: Widget.id
- Used to generate an ``id`` attribute on the generated DOM
- root. Note that this is rarely needed, and is probably not a good idea
- if a widget can be used more than once.
+ Used to generate an ``id`` attribute on the generated DOM
+ root. Note that this is rarely needed, and is probably not a good idea
+ if a widget can be used more than once.
- .. attribute:: Widget.className
+.. attribute:: Widget.className
- Used to generate a ``class`` attribute on the generated DOM root. Note
- that it can actually contain more than one css class:
- *'some-class other-class'*
+ Used to generate a ``class`` attribute on the generated DOM root. Note
+ that it can actually contain more than one css class:
+ *'some-class other-class'*
- .. attribute:: Widget.attributes
+.. attribute:: Widget.attributes
- Mapping (object literal) of attribute names to attribute
- values. Each of these k:v pairs will be set as a DOM attribute
- on the generated DOM root.
+ Mapping (object literal) of attribute names to attribute
+ values. Each of these k:v pairs will be set as a DOM attribute
+ on the generated DOM root.
.. attribute:: Widget.el
raw DOM element set as root to the widget (only available after the start
- lifecyle method)
+ lifecycle method)
.. attribute:: Widget.$el
jQuery wrapper around :attr:`~Widget.el`. (only available after the start
- lifecyle method)
+ lifecycle method)
.. attribute:: Widget.template
@@ -651,13 +651,21 @@ Widget API
initialized but before it has been started. The root element generated by
the template will be set as the DOM root of the widget.
-.. attribute:: xmlDependencies
+.. attribute:: Widget.xmlDependencies
List of paths to xml files that need to be loaded before the
widget can be rendered. This will not induce loading anything that has already
- been loaded.
+ been loaded. This is useful when you want to load your templates lazily,
+ or if you want to share a widget between the website and the web client
+ interface.
-.. attribute:: events
+ .. code-block:: javascript
+
+ var EditorMenuBar = Widget.extend({
+ xmlDependencies: ['/web_editor/static/src/xml/editor.xml'],
+ ...
+
+.. attribute:: Widget.events
Events are a mapping of an event selector (an event name and an optional
CSS selector separated by a space) to a callback. The callback can
@@ -682,7 +690,7 @@ Widget API
Note: the use of an inline function is discouraged, and will probably be
removed sometimes in the future.
-.. attribute:: custom_events
+.. attribute:: Widget.custom_events
this is almost the same as the *events* attribute, but the keys
are arbitrary strings. They represent business events triggered by
@@ -1142,6 +1150,127 @@ may need to directly call a controller (available on some route).
params: { some: kwargs},
});
+Notifications
+==============
+
+The Odoo framework has a standard way to communicate various informations to the
+user: notifications, which are displayed on the top right of the user interface.
+
+There are two types of notifications:
+
+- *notification*: useful to display some feedback. For example, whenever a user
+ unsubscribed to a channel.
+
+- *warning*: useful to display some important/urgent information. Typically
+ most kind of (recoverable) errors in the system.
+
+Also, notifications can be used to ask a question to the user without disturbing
+its workflow. Imagine a phone call received through VOIP: a sticky notification
+could be displayed with two buttons *Accept* and *Decline*.
+
+Notification system
+-------------------
+
+The notification system in Odoo is designed with the following components:
+
+- a *Notification* widget: this is a simple widget that is meant to be created
+ and displayed with the desired information
+
+- a *NotificationService*: a service whose responsibility is to create and
+ destroy notifications whenever a request is done (with a custom_event). Note
+ that the web client is a service provider.
+
+- two helper functions in *ServiceMixin*: *do_notify* and *do_warn*
+
+
+Displaying a notification
+-------------------------
+The most common way to display a notification is by using two methods that come
+from the *ServiceMixin*:
+
+- *do_notify(title, message, sticky, className)*:
+ Display a notification of type *notification*.
+
+ - *title*: string. This will be displayed on the top as a title
+
+ - *message*: string, the content of the notification
+
+ - *sticky*: boolean, optional. If true, the notification will stay until the
+ user dismisses it. Otherwise, the notification will be automatically
+ closed after a short delay.
+
+ - *className*: string, optional. This is a css class name that will be
+ automatically added to the notification. This could be useful for styling
+ purpose, even though its use is discouraged.
+
+- *do_warn(title, message, sticky, className)*:
+ Display a notification of type *warning*.
+
+ - *title*: string. This will be displayed on the top as a title
+
+ - *message*: string, the content of the notification
+
+ - *sticky*: boolean, optional. If true, the notification will stay until the
+ user dismisses it. Otherwise, the notification will be automatically
+ closed after a short delay.
+
+ - *className*: string, optional. This is a css class name that will be
+ automatically added to the notification. This could be useful for styling
+ purpose, even though its use is discouraged.
+
+Here are two examples on how to use these methods:
+
+.. code-block:: javascript
+
+ // note that we call _t on the text to make sure it is properly translated.
+ this.do_notify(_t("Success"), _t("Your signature request has been sent."));
+
+ this.do_warn(_t("Error"), _t("Filter name is required."));
+
+
+Systray
+=======
+
+The Systray is the right part of the menu bar in the interface, where the web
+client displays a few widgets, such as a messaging menu.
+
+When the SystrayMenu is created by the menu, it will look for all registered
+widgets and add them as a sub widget at the proper place.
+
+There is currently no specific API for systray widgets. They are supposed to
+be simple widgets, and can communicate with their environment just like other
+widgets with the *trigger_up* method.
+
+Adding a new Systray Item
+-------------------------
+
+There is no systray registry. The proper way to add a widget is to add it to
+the class variable SystrayMenu.items.
+
+.. code-block:: javascript
+
+ var SystrayMenu = require('web.SystrayMenu');
+
+ var MySystrayWidget = Widget.extend({
+ ...
+ });
+
+ SystrayMenu.Items.push(MySystrayWidget);
+
+
+Ordering
+--------
+
+Before adding the widget to himself, the Systray Menu will sort the items by
+a sequence property. If that property is not present on the prototype, it will
+use 50 instead. So, to position a systray item to be on the right, one can
+set a very high sequence number (and conversely, a low number to put it on the
+left).
+
+.. code-block:: javascript
+
+ MySystrayWidget.prototype.sequence = 100;
+
Translation management
======================
@@ -1160,7 +1289,7 @@ to be translated. The way it currently works is the following:
is found.
Note that translations are explained in more details, from the server point of
-view, in the document :doc:`translations`.
+view, in the document :doc:`translations`.
There are two important functions for the translations in javascript: *_t* and
*_lt*. The difference is that *_lt* is lazily evaluated.
@@ -1187,6 +1316,55 @@ when the module is loaded.
Note that translation functions need some care. The string given in argument
should not be dynamic.
+Session
+=======
+
+There is a specific module provided by the web client which contains some
+information specific to the user current *session*. Some notable keys are
+
+- uid: the current user ID (its ID as a *res.users*)
+- user_name: the user name, as a string
+- the user context (user ID, language and timezone)
+- partner_id: the ID of the partner associated to the current user
+- db: the name of the database currently being in use
+
+Adding information to the session
+---------------------------------
+
+When the /web route is loaded, the server will inject some session information
+in the template a script tag. The information will be read from the method
+*session_info* of the model *ir.http*. So, if one wants to add a specific
+information, it can be done by overriding the session_info method and adding it
+to the dictionary.
+
+.. code-block:: python
+
+ from odoo import models
+ from odoo.http import request
+
+
+ class IrHttp(models.AbstractModel):
+ _inherit = 'ir.http'
+
+ def session_info(self):
+ result = super(IrHttp, self).session_info()
+ result['some_key'] = get_some_value_from_db()
+ return result
+
+Now, the value can be obtained in javascript by reading it in the session:
+
+.. code-block:: javascript
+
+ var session = require('web.session');
+ var myValue = session.some_key;
+ ...
+
+Note that this mechanism is designed to reduce the amount of communication
+needed by the web client to be ready. It is more appropriate for data which is
+cheap to compute (a slow session_info call will delay the loading for the web
+client for everyone), and for data which is required early in the initialization
+process.
+
Views
======
@@ -1280,6 +1458,34 @@ Let us mention the most important ones:
- the field widgets can be used outside of a view. Their API is slightly
awkward, but they are designed to be standalone.
+Decorations
+-----------
+
+Like the list view, field widgets have a simple support for decorations. The
+goal of decorations is to have a simple way to specify a text color depending on
+the record current state. For example,
+
+.. code-block:: xml
+
+ <field name="state" decoration-danger="amount &lt; 10000"/>
+
+The valid decoration names are:
+
+- decoration-bf
+- decoration-it
+- decoration-danger
+- decoration-info
+- decoration-muted
+- decoration-primary
+- decoration-success
+- decoration-warning
+
+Each decoration *decoration-X* will be mapped to a css class *text-X*, which is
+a standard bootstrap css class (except for *text-it* and *text-bf*, which are
+handled by odoo and correspond to italic and bold, respectively). Note that the
+value of the decoration attribute should be a valid python expression, which
+will be evaluated with the record as evaluation context.
+
Non relational fields
---------------------
@@ -1291,6 +1497,24 @@ order.
- Supported field types: *integer*
+ Options:
+
+ - type: setting the input type (*text* by default, can be set on *number*)
+
+ On edit mode, the field is rendered as an input with the HTML attribute type
+ setted on *number* (so user can benefit the native support, especially on
+ mobile). In this case, the default formatting is disabled to avoid incompability.
+
+ .. code-block:: xml
+
+ <field name="int_value" options='{"type": "number"}'/>
+
+ - step: set the step to the value up and down when the user click on buttons
+ (only for input of type number, 1 by default)
+
+ .. code-block:: xml
+
+ <field name="int_value" options='{"type": "number", "step": 100}'/>
- float (FieldFloat)
This is the default field type for fields of type *float*.
@@ -1305,6 +1529,25 @@ order.
<field name="factor" digits="[42,5]"/>
+ Options:
+
+ - type: setting the input type (*text* by default, can be set on *number*)
+
+ On edit mode, the field is rendered as an input with the HTML attribute type
+ setted on *number* (so user can benefit the native support, especially on
+ mobile). In this case, the default formatting is disabled to avoid incompability.
+
+ .. code-block:: xml
+
+ <field name="int_value" options='{"type": "number"}'/>
+
+ - step: set the step to the value up and down when the user click on buttons
+ (only for input of type number, 1 by default)
+
+ .. code-block:: xml
+
+ <field name="int_value" options='{"type": "number", "step": 0.1}'/>
+
- float_time (FieldFloatTime)
The goal of this widget is to display properly a float value that represents
a time interval (in hours). So, for example, 0.5 should be formatted as 0:30,
@@ -1312,6 +1555,26 @@ order.
- Supported field types: *float*
+- float_factor (FieldFloatFactor)
+ This widget aims to display properly a float value that converted using a factor
+ given in its options. So, for example, the value saved in database is 0.5 and the
+ factor is 3, the widget value should be formatted as 1.5.
+
+ - Supported field types: *float*
+
+- float_toggle (FieldFloatToggle)
+ The goal of this widget is to replace the input field by a button containing a
+ range of possible values (given in the options). Each click allows the user to loop
+ in the range. The purpose here is to restrict the field value to a predefined selection.
+ Also, the widget support the factor conversion as the *float_factor* widget (Range values
+ should be the result of the conversion).
+
+ - Supported field types: *float*
+
+ .. code-block:: xml
+
+ <field name="days_to_close" widget="float_toggle" options='{"factor": 2, "range": [0, 4, 8]}'/>
+
- boolean (FieldBoolean)
This is the default field type for fields of type *boolean*.
@@ -1372,9 +1635,11 @@ order.
- handle (HandleWidget)
- This field's job is to be displayed as a *handle* in a list view, and allow
+ This field's job is to be displayed as a *handle* in a list view, and allows
reordering the various records by drag and dropping lines.
+ .. warning:: Having more than one field with a handle widget on the same list is not supported.
+
- Supported field types: *integer*
@@ -1611,7 +1876,7 @@ order.
- current_value: get the current_value from the field that must be present in the view
- max_value: get the max_value from the field that must be present in the view
- edit_max_value: boolean if the max_value is editable
- - title: title of the bar, displayed on top of the bar --> not translated,
+ - title: title of the bar, displayed on top of the bar --> not translated,
use parameter (not option) "title" instead
.. code-block:: xml
@@ -1687,13 +1952,23 @@ Relational fields
Options:
- - horizontal: if true, radio buttons will be diplayed horizontally.
+ - horizontal: if true, radio buttons will be displayed horizontally.
.. code-block:: xml
<field name="recommended_activity_type_id" widget="radio"
options="{'horizontal':true}"/>
+- selection_badge (FieldSelectionBadge)
+ This is a subfield of FieldSelection, but specialized to display all the
+ valid choices as rectangular badges.
+
+ - Supported field types: *selection, many2one*
+
+ .. code-block:: xml
+
+ <field name="recommended_activity_type_id" widget="selection_badge"/>
+
- many2one (FieldMany2One)
Default widget for many2one fields.
@@ -1738,7 +2013,7 @@ Relational fields
- Supported field types: *many2one*
- many2many (FieldMany2Many)
- Defaut widget for many2many fields.
+ Default widget for many2many fields.
- Supported field types: *many2many*
@@ -1792,7 +2067,7 @@ Relational fields
- Supported field types: *many2many*
- one2many (FieldOne2Many)
- Defaut widget for one2many fields.
+ Default widget for one2many fields.
It usually displays data in a sub list view, or a sub kanban view.
@@ -1823,6 +2098,122 @@ Relational fields
This widget is exactly the same as a FieldOne2Many. It is registered at this
key only for backward compatibility reasons. Please avoid using this.
+
+Client actions
+==============
+
+The idea of a client action is a customized widget that is integrated in the
+web client interface, just like a *act_window_action*. This is useful when
+you need a component that is not closely linked to an existing view or a
+specific model. For example, the Discuss application is actually a client
+action.
+
+A client action is a term that has various meanings, depending on the context:
+
+- from the perspective of the server, it is a record of the model *ir_action*,
+ with a field *tag* of type char
+- from the perspective of the web client, it is a widget, which inherit from
+ the class AbstractAction, and is supposed to be registered in the
+ action registry under the corresponding key (from the field char)
+
+Whenever a menu item is associated to a client action, opening it will simply
+fetch the action definition from the server, then lookup into its action
+registry to get the Widget definition at the appropriate key, and finally, it
+will instantiate and append the widget to the proper place in the DOM.
+
+Adding a client action
+----------------------
+
+A client action is a widget which will control the part of the screen below the
+menu bar. It can have a control panel, if necessary. Defining a client action
+can be done in two steps: implementing a new widget, and registering the widget
+in the action registry.
+
+- Implementing a new client action:
+ This is done by creating a widget:
+
+ .. code-block:: javascript
+
+ var ControlPanelMixin = require('web.ControlPanelMixin');
+ var AbstractAction = require('web.AbstractAction');
+
+ var ClientAction = AbstractAction.extend(ControlPanelMixin, {
+ ...
+ });
+
+ Do not add the controlpanel mixin if you do not need it. Note that some
+ code is needed to interact with the control panel (via the
+ ``update_control_panel`` method given by the mixin).
+
+- Registering the client action:
+ As usual, we need to make the web client aware of the mapping between
+ client actions and the actual class:
+
+ .. code-block:: javascript
+
+ var core = require('web.core');
+
+ core.action_registry.add('my-custom-action', ClientAction);
+
+
+ Then, to use the client action in the web client, we need to create a client
+ action record (a record of the model ``ir.actions.client``) with the proper
+ ``tag`` attribute:
+
+ .. code-block:: xml
+
+ <record id="my_client_action" model="ir.actions.client">
+ <field name="name">Some Name</field>
+ <field name="tag">my-custom-action</field>
+ </record>
+
+
+Using the control panel mixin
+-----------------------------
+
+By default, the AbstractAction class does not include the control panel mixin.
+This means that a client action does not display a control panel. In order to
+do that, several steps should be done.
+
+- add ControlPanelMixin in the widget:
+
+ .. code-block:: javascript
+
+ var ControlPanelMixin = require('web.ControlPanelMixin');
+
+ var MyClientAction = AbstractAction.extend(ControlPanelMixin, {
+ ...
+ });
+
+- call the method *update_control_panel* whenever we need to update the control
+ panel. For example:
+
+ .. code-block:: javascript
+
+ var SomeClientAction = Widget.extend(ControlPanelMixin, {
+ ...
+ start: function () {
+ this._renderButtons();
+ this._updateControlPanel();
+ ...
+ },
+ do_show: function () {
+ ...
+ this._updateControlPanel();
+ },
+ _renderButtons: function () {
+ this.$buttons = $(QWeb.render('SomeTemplate.Buttons'));
+ this.$buttons.on('click', ...);
+ },
+ _updateControlPanel: function () {
+ this.update_control_panel({
+ cp_content: {
+ $buttons: this.$buttons,
+ },
+ });
+
+For more information, look into the *control_panel.js* file.
+
.. _.appendTo():
http://api.jquery.com/appendTo/