summaryrefslogtreecommitdiff
path: root/addons/web/doc/guidelines.rst
blob: 8721d372f5b2f35c2ee870bc1f551bfe2985ba82 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
Guidelines and Recommendations
==============================

Web Module Recommendations
--------------------------

Identifiers (``id`` attribute) should be avoided
''''''''''''''''''''''''''''''''''''''''''''''''

In generic applications and modules, ``@id`` limits the reusabily of
components and tends to make code more brittle.

Just about all the time, they can be replaced with nothing, with
classes or with keeping a reference to a DOM node or a jQuery element
around.

.. note::

    If it is *absolutely necessary* to have an ``@id`` (because a
    third-party library requires one and can't take a DOM element), it
    should be generated with `_.uniqueId
    <http://underscorejs.org/#uniqueId>`_ or some other similar
    method.

Avoid predictable/common CSS class names
''''''''''''''''''''''''''''''''''''''''

Class names such as "content" or "navigation" might match the desired
meaning/semantics, but it is likely an other developer will have the
same need, creating a naming conflict and unintended behavior. Generic
class names should be prefixed with e.g. the name of the component
they belong to (creating "informal" namespaces, much as in C or
Objective-C)

Global selectors should be avoided
''''''''''''''''''''''''''''''''''

Because a component may be used several times in a single page (an
example in OpenERP is dashboards), queries should be restricted to a
given component's scope. Unfiltered selections such as ``$(selector)``
or ``document.querySelectorAll(selector)`` will generally lead to
unintended or incorrect behavior.

OpenERP Web's :js:class:`~openerp.web.Widget` has an attribute
providing its DOM root :js:attr:`Widget.$el <openerp.web.Widget.$el>`,
and a shortcut to select nodes directly :js:attr:`Widget.$
<openerp.web.Widget.$>`.

More generally, never assume your components own or controls anything
beyond its own personal DOM.

Understand deferreds
''''''''''''''''''''

Deferreds, promises, futures, …

Known under many names, these objects are essential to and (in OpenERP
Web) widely used for making :doc:`asynchronous javascript operations
<async>` palatable and understandable.

OpenERP Web guidelines
----------------------

* HTML templating/rendering should use :doc:`qweb` unless absolutely
  trivial.

* All interactive components (components displaying information to the
  screen or intercepting DOM events) must inherit from
  :class:`~openerp.web.Widget` and correctly implement and use its API
  and lifecycle.

* All css classes must be prefixed with *oe_* .

* Asynchronous functions (functions which call :ref:`session.rpc
  <rpc_rpc>` directly or indirectly at the very least) *must* return
  deferreds, so that callers of overriders can correctly synchronize
  with them.

New Javascript guidelines
-------------------------

From v11, we introduce a new coding standard for Odoo Javascript code.  Here it
is:

* add "use strict"; on top of every odoo JS module

* name all entities exported by a JS module. So, instead of 

  .. code-block:: javascript

      return Widget.extend({
        ...
      });

you should use:

  .. code-block:: javascript

      var MyWidget = Widget.extend({
        ...
      });

      return MyWidget

* there should be one space between function and the left parenthesis:

  .. code-block:: javascript

    function (a, b) {}

* JS files should have a (soft) limit of 80 chars width, and a hard limit of 100

* document every functions and every files, with the style JSDoc.

* for function overriding other functions, consider adding the tag @override in
  the JS Doc.  Also, you can mention which method is overridden:

  .. code-block:: javascript

    /**
     * When a save operation has been confirmed from the model, this method is
     * called.
     *
     * @override method from field manager mixin
     * @param {string} id
     * @returns {Deferred}
     */
    _confirmSave: function (id) {

* there should be an empty line between the main function comments and the tags,
  or parameter descriptions

* avoid introspection: don't build dynamically a method name and call it.  It is
  more fragile and more difficult to refactor

* methods should be private if possible

* never read an attribute of an attribute on somethig that you have a reference.
  So, this is not good:

  .. code-block:: javascript

    this.myObject.propA.propB

* never use a reference to the parent widget

* avoid using the 'include' functionality: extending a class is fine and does
  not cause issue, including a class is much more fragile, and may not work.

* For the widgets, here is how the various attributes/functions should be
  ordered:

  1. all static attributes, such as template, events, custom_events, ...

  2. all methods from the lifecycle of a widget, in this order: init, willStart,
     start, destroy

  3. If there are public methods, a section titled "Public", with an empty line
    before and after

  4. all public methods, camelcased, in alphabetic order

  5. If there are private methods, a section titled "Private", with an empty line
    before and after

  6. all private methods, camelcased and prefixed with _, in alphabetic order

  7. If there are event handlers, a section titled "Handlers", with an empty line
    before and after

  8. all handlers, camelcased and prefixed with _on, in alphabetic order

  9. If there are static methods, they should be in a section titled "Static".
     All static methods are considered public, camelcased with no _.

* write unit tests

* for the event handlers defined by the key 'event' or 'custom_events', don't
  inline the function.  Always add a string name, and add the definition in the
  handler section

* one space after if and for

* never call private methods on another object

* object definition on more than one line: each element should have a trailing
  comma.

* strings: double quotes for all textual strings (such as "Hello"), and single
  quotes for all other strings, such as a css selector '.o_form_view'

* always use this._super.apply(this, arguments);

* keys in an object: ordered by alphabetic order