[FIX] exception when logging in the web client and the server was launched using...
[odoo/odoo.git] / addons / web / doc / module.rst
1 Building an OpenERP Web module
2 ==============================
3
4 There is no significant distinction between an OpenERP Web module and
5 an OpenERP module, the web part is mostly additional data and code
6 inside a regular OpenERP module. This allows providing more seamless
7 features by integrating your module deeper into the web client.
8
9 A Basic Module
10 --------------
11
12 A very basic OpenERP module structure will be our starting point:
13
14 .. code-block:: text
15
16     web_example
17     ├── __init__.py
18     └── __openerp__.py
19
20 .. literalinclude:: module/__openerp__.py
21     :language: python
22
23 This is a sufficient minimal declaration of a valid OpenERP module.
24
25 Web Declaration
26 ---------------
27
28 There is no such thing as a "web module" declaration. An OpenERP
29 module is automatically recognized as "web-enabled" if it contains a
30 ``static`` directory at its root, so:
31
32 .. code-block:: text
33
34     web_example
35     ├── __init__.py
36     ├── __openerp__.py
37     └── static
38
39 is the extent of it. You should also change the dependency to list
40 ``web``:
41
42 .. literalinclude:: module/__openerp__.py.1.diff
43     :language: diff
44
45 .. note::
46
47     This does not matter in normal operation so you may not realize
48     it's wrong (the web module does the loading of everything else, so
49     it can only be loaded), but when e.g. testing the loading process
50     is slightly different than normal, and incorrect dependency may
51     lead to broken code.
52
53 This makes the "web" discovery system consider the module as having a
54 "web part", and check if it has web controllers to mount or javascript
55 files to load. The content of the ``static/`` folder is also
56 automatically made available to web browser at the URL
57 ``$module-name/static/$file-path``. This is sufficient to provide
58 pictures (of cats, usually) through your module. However there are
59 still a few more steps to running javascript code.
60
61 Getting Things Done
62 -------------------
63
64 The first one is to add javascript code. It's customary to put it in
65 ``static/src/js``, to have room for e.g. other file types, or
66 third-party libraries.
67
68 .. literalinclude:: module/static/src/js/first_module.js
69     :language: javascript
70
71 The client won't load any file unless specified, thus the new file
72 should be listed in the module's manifest file, under a new key ``js``
73 (a list of file names, or glob patterns):
74
75 .. literalinclude:: module/__openerp__.py.2.diff
76     :language: diff
77
78 At this point, if the module is installed and the client reloaded the
79 message should appear in your browser's development console.
80
81 .. note::
82
83     Because the manifest file has been edited, you will have to
84     restart the OpenERP server itself for it to be taken in account.
85
86     You may also want to open your browser's console *before*
87     reloading, depending on the browser messages printed while the
88     console is closed may not work or may not appear after opening it.
89
90 .. note::
91
92     If the message does not appear, try cleaning your browser's caches
93     and ensure the file is correctly loaded from the server logs or
94     the "resources" tab of your browser's developers tools.
95
96 At this point the code runs, but it runs only once when the module is
97 initialized, and it can't get access to the various APIs of the web
98 client (such as making RPC requests to the server). This is done by
99 providing a `javascript module`_:
100
101 .. literalinclude:: module/static/src/js/first_module.js.1.diff
102     :language: diff
103
104 If you reload the client, you'll see a message in the console exactly
105 as previously. The differences, though invisible at this point, are:
106
107 * All javascript files specified in the manifest (only this one so
108   far) have been fully loaded
109 * An instance of the web client and a namespace inside that instance
110   (with the same name as the module) have been created and are
111   available for use
112
113 The latter point is what the ``instance`` parameter to the function
114 provides: an instance of the OpenERP Web client, with the contents of
115 all the new module's dependencies loaded in and initialized. These are
116 the entry points to the web client's APIs.
117
118 To demonstrate, let's build a simple :doc:`client action
119 <client_action>`: a stopwatch
120
121 First, the action declaration:
122
123 .. literalinclude:: module/__openerp__.py.3.diff
124     :language: diff
125
126 .. literalinclude:: module/web_example.xml
127     :language: xml
128
129 then set up the :doc:`client action hook <client_action>` to register
130 a function (for now):
131
132 .. literalinclude:: module/static/src/js/first_module.js.2.diff
133     :language: diff
134
135 Updating the module (in order to load the XML description) and
136 re-starting the server should display a new menu *Example Client
137 Action* at the top-level. Opening said menu will make the message
138 appear, as usual, in the browser's console.
139
140 Paint it black
141 --------------
142
143 The next step is to take control of the page itself, rather than just
144 print little messages in the console. This we can do by replacing our
145 client action function by a :doc:`widget`. Our widget will simply use
146 its :js:func:`~openerp.web.Widget.start` to add some content to its
147 DOM:
148
149 .. literalinclude:: module/static/src/js/first_module.js.3.diff
150     :language: diff
151
152 after reloading the client (to update the javascript file), instead of
153 printing to the console the menu item clears the whole screen and
154 displays the specified message in the page.
155
156 Since we've added a class on the widget's :ref:`DOM root
157 <widget-dom_root>` we can now see how to add a stylesheet to a module:
158 first create the stylesheet file:
159
160 .. literalinclude:: module/static/src/css/web_example.css
161     :language: css
162
163 then add a reference to the stylesheet in the module's manifest (which
164 will require restarting the OpenERP Server to see the changes, as
165 usual):
166
167 .. literalinclude:: module/__openerp__.py.4.diff
168     :language: diff
169
170 the text displayed by the menu item should now be huge, and
171 white-on-black (instead of small and black-on-white). From there on,
172 the world's your canvas.
173
174 .. note::
175
176     Prefixing CSS rules with both ``.openerp`` (to ensure the rule
177     will apply only within the confines of the OpenERP Web client) and
178     a class at the root of your own hierarchy of widgets is strongly
179     recommended to avoid "leaking" styles in case the code is running
180     embedded in an other web page, and does not have the whole screen
181     to itself.
182
183 So far we haven't built much (any, really) DOM content. It could all
184 be done in :js:func:`~openerp.web.Widget.start` but that gets unwieldy
185 and hard to maintain fast. It is also very difficult to extend by
186 third parties (trying to add or change things in your widgets) unless
187 broken up into multiple methods which each perform a little bit of the
188 rendering.
189
190 The first way to handle this method is to delegate the content to
191 plenty of sub-widgets, which can be individually overridden. An other
192 method [#DOM-building]_ is to use `a template
193 <http://en.wikipedia.org/wiki/Web_template>`_ to render a widget's
194 DOM.
195
196 OpenERP Web's template language is :doc:`qweb`. Although any
197 templating engine can be used (e.g. `mustache
198 <http://mustache.github.com/>`_ or `_.template
199 <http://underscorejs.org/#template>`_) QWeb has important features
200 which other template engines may not provide, and has special
201 integration to OpenERP Web widgets.
202
203 Adding a template file is similar to adding a style sheet:
204
205 .. literalinclude:: module/static/src/xml/web_example.xml
206     :language: xml
207
208 .. literalinclude:: module/__openerp__.py.5.diff
209     :language: diff
210
211 The template can then easily be hooked in the widget:
212
213 .. literalinclude:: module/static/src/js/first_module.js.4.diff
214     :language: diff
215
216 And finally the CSS can be altered to style the new (and more complex)
217 template-generated DOM, rather than the code-generated one:
218
219 .. literalinclude:: module/static/src/css/web_example.css.1.diff
220     :language: diff
221
222 .. note::
223
224     The last section of the CSS change is an example of "state
225     classes": a CSS class (or set of classes) on the root of the
226     widget, which is toggled when the state of the widget changes and
227     can perform drastic alterations in rendering (usually
228     showing/hiding various elements).
229
230     This pattern is both fairly simple (to read and understand) and
231     efficient (because most of the hard work is pushed to the
232     browser's CSS engine, which is usually highly optimized, and done
233     in a single repaint after toggling the class).
234
235 The last step (until the next one) is to add some behavior and make
236 our stopwatch watch. First hook some events on the buttons to toggle
237 the widget's state:
238
239 .. literalinclude:: module/static/src/js/first_module.js.5.diff
240     :language: diff
241
242 This demonstrates the use of the "events hash" and event delegation to
243 declaratively handle events on the widget's DOM. And already changes
244 the button displayed in the UI. Then comes some actual logic:
245
246 .. literalinclude:: module/static/src/js/first_module.js.6.diff
247     :language: diff
248
249 * An initializer (the ``init`` method) is introduced to set-up a few
250   internal variables: ``_start`` will hold the start of the timer (as
251   a javascript Date object), and ``_watch`` will hold a ticker to
252   update the interface regularly and display the "current time".
253
254 * ``update_counter`` is in charge of taking the time difference
255   between "now" and ``_start``, formatting as ``HH:MM:SS`` and
256   displaying the result on screen.
257
258 * ``watch_start`` is augmented to initialize ``_start`` with its value
259   and set-up the update of the counter display every 33ms.
260
261 * ``watch_stop`` disables the updater, does a final update of the
262   counter display and resets everything.
263
264 * Finally, because javascript Interval and Timeout objects execute
265   "outside" the widget, they will keep going even after the widget has
266   been destroyed (especially an issue with intervals as they repeat
267   indefinitely). So ``_watch`` *must* be cleared when the widget is
268   destroyed (then the ``_super`` must be called as well in order to
269   perform the "normal" widget cleanup).
270
271 Starting and stopping the watch now works, and correctly tracks time
272 since having started the watch, neatly formatted.
273
274 .. [#DOM-building] they are not alternative solutions: they work very
275                    well together. Templates are used to build "just
276                    DOM", sub-widgets are used to build DOM subsections
277                    *and* delegate part of the behavior (e.g. events
278                    handling).
279
280 .. _javascript module:
281     http://addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript