diff options
Diffstat (limited to 'doc/reference/javascript_reference.rst')
| -rw-r--r-- | doc/reference/javascript_reference.rst | 463 |
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 < 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/ |
