[FIX] doc: typos and code samples from howtos
[odoo/odoo.git] / doc / howtos / web.rst
1 =============================
2 Building Interface Extensions
3 =============================
4
5 .. highlight:: javascript
6
7 .. default-domain:: js
8
9 This guide is about creating modules for Odoo's web client.
10
11 To create websites with Odoo, see :doc:`website`; to add business capabilities
12 or extend existing business systems of Odoo, see :doc:`backend`.
13
14 .. warning::
15
16     This guide assumes knowledge of:
17
18     * Javascript basics and good practices
19     * jQuery_
20     * `Underscore.js`_
21
22     It also requires an installed Odoo, and Git_.
23
24
25 A Simple Module
26 ===============
27
28 Let's start with a simple Odoo module holding basic web component
29 configuration and letting us test the web framework.
30
31 The example module is available online and can be downloaded using the
32 following command:
33
34 .. code-block:: console
35
36     $ git clone http://github.com/odoo/petstore
37
38 This will create a ``petstore`` folder wherever you executed the command.
39 You then need to add that folder to Odoo's
40 :option:`addons path <odoo.py --addons-path>`, create a new database and
41 install the ``oepetstore`` module.
42
43 If you browse the ``petstore`` folder, you should see the following content:
44
45 .. code-block:: text
46
47     oepetstore
48     |-- images
49     |   |-- alligator.jpg
50     |   |-- ball.jpg
51     |   |-- crazy_circle.jpg
52     |   |-- fish.jpg
53     |   `-- mice.jpg
54     |-- __init__.py
55     |-- oepetstore.message_of_the_day.csv
56     |-- __openerp__.py
57     |-- petstore_data.xml
58     |-- petstore.py
59     |-- petstore.xml
60     `-- static
61         `-- src
62             |-- css
63             |   `-- petstore.css
64             |-- js
65             |   `-- petstore.js
66             `-- xml
67                 `-- petstore.xml
68
69 The module already holds various server customizations. We'll come back to
70 these later, for now let's focus on the web-related content, in the ``static``
71 folder.
72
73 Files used in the "web" side of an Odoo module must be placed in a ``static``
74 folder so they are available to a web browser, files outside that folder can
75 not be fetched by browsers. The ``src/css``, ``src/js`` and ``src/xml``
76 sub-folders are conventional and not strictly necessary.
77
78 ``oepetstore/static/css/petstore.css``
79     currently empty, will hold the CSS_ for pet store content
80 ``oepetstore/static/xml/petstore.xml``
81     Mostly empty, will hold :ref:`reference/qweb` templates
82 ``oepetstore/static/js/petstore.js``
83     The most important (and interesting) part, contains the logic of the
84     application (or at least its web-browser side) as javascript. It should
85     currently look like::
86
87         openerp.oepetstore = function(instance, local) {
88             var _t = instance.web._t,
89                 _lt = instance.web._lt;
90             var QWeb = instance.web.qweb;
91
92             local.HomePage = instance.Widget.extend({
93                 start: function() {
94                     console.log("pet store home page loaded");
95                 },
96             });
97
98             instance.web.client_actions.add(
99                 'petstore.homepage', 'instance.oepetstore.HomePage');
100         }
101
102 Which only prints a small message in the browser's console.
103
104 .. warning::
105
106     All JavaScript files are concatenated and :term:`minified` to improve
107     application load time.
108
109     One of the drawback is debugging becomes more difficult as
110     individual files disappear and the code is made significantly less
111     readable. It is possible to disable this process by enabling the
112     "developer mode": log into your Odoo instance (user *admin* password
113     *admin* by default) open the user menu (in the top-right corner of the
114     Odoo screen) and select :guilabel:`About Odoo` then :guilabel:`Activate
115     the developer mode`:
116
117     .. image:: web/about_odoo.png
118         :align: center
119
120     .. image:: web/devmode.png
121         :align: center
122
123     This will reload the web client with optimizations disabled, making
124     development and debugging significantly more comfortable.
125
126 .. todo:: qweb files hooked via __openerp__.py, but js and CSS use bundles
127
128 Odoo JavaScript Module
129 ======================
130
131 Javascript doesn't have built-in modules. As a result variables defined in
132 different files are all mashed together and may conflict. This has given rise
133 to various module patterns used to build clean namespaces and limit risks of
134 naming conflicts.
135
136 The Odoo framework uses one such pattern to define modules within web addons,
137 in order to both namespace code and correctly order its loading.
138
139 ``oepetstore/static/js/petstore.js`` contains a module declaration::
140
141     openerp.oepetstore = function(instance, local) {
142         local.xxx = ...;
143     }
144
145 In Odoo web, modules are declared as functions set on the global ``openerp``
146 variable. The function's name must be the same as the addon (in this case
147 ``oepetstore``) so the framework can find it, and automatically initialize it.
148
149 When the web client decides to load your module, it'll call the root function
150 and provide two parameters:
151
152 * the first parameter is the current instance of the Odoo web client, it gives
153   access to various capabilities defined by the Odoo (translations,
154   network services) as well as objects defined by the core or by other
155   modules.
156 * the second parameter is your own local namespace automatically created by
157   the web client. Objects and variables which should be accessible from
158   outside your module (either because the Odoo web client needs to call them
159   or because others may want to customize them) should be set inside that
160   namespace.
161
162 Classes
163 =======
164
165 Much as modules, and contrary to most object-oriented languages, javascript
166 does not build in *classes*\ [#classes]_ although it provides roughly
167 equivalent (if lower-level and more verbose) mechanisms.
168
169 For simplicity and developer-friendliness purposes, Odoo web provides a class
170 system based on John Resig's `Simple JavaScript Inheritance`_.
171
172 New classes are defined by calling the :func:`~openerp.web.Class.extend`
173 method of :class:`openerp.web.Class`::
174
175     var MyClass = instance.web.Class.extend({
176         say_hello: function() {
177             console.log("hello");
178         },
179     });
180
181 The :func:`~openerp.web.Class.extend` method takes a dictionary describing
182 the new class's content (methods and static attributes). In this case, it will
183 only have a ``say_hello`` method which takes no parameters.
184
185 Classes are instantiated using the ``new`` operator::
186
187     var my_object = new MyClass();
188     my_object.say_hello();
189     // print "hello" in the console
190
191 And attributes of the instance can be accessed via ``this``::
192
193     var MyClass = instance.web.Class.extend({
194         say_hello: function() {
195             console.log("hello", this.name);
196         },
197     });
198
199     var my_object = new MyClass();
200     my_object.name = "Bob";
201     my_object.say_hello();
202     // print "hello Bob" in the console
203
204 Classes can provide an initializer to perform the initial setup of the
205 instance, by defining an ``init()`` method. The initializer receives the
206 parameters passed when using the ``new`` operator::
207
208     var MyClass = instance.web.Class.extend({
209         init: function(name) {
210             this.name = name;
211         },
212         say_hello: function() {
213             console.log("hello", this.name);
214         },
215     });
216
217     var my_object = new MyClass("Bob");
218     my_object.say_hello();
219     // print "hello Bob" in the console
220
221 It is also possible to create subclasses from existing (used-defined) classes
222 by calling :func:`~openerp.web.Class.extend` on the parent class, as is done
223 to subclass :class:`~openerp.web.Class`::
224
225     var MySpanishClass = MyClass.extend({
226         say_hello: function() {
227             console.log("hola", this.name);
228         },
229     });
230
231     var my_object = new MySpanishClass("Bob");
232     my_object.say_hello();
233     // print "hola Bob" in the console
234
235 When overriding a method using inheritance, you can use ``this._super()`` to
236 call the original method::
237
238     var MySpanishClass = MyClass.extend({
239         say_hello: function() {
240             this._super();
241             console.log("translation in Spanish: hola", this.name);
242         },
243     });
244
245     var my_object = new MySpanishClass("Bob");
246     my_object.say_hello();
247     // print "hello Bob \n translation in Spanish: hola Bob" in the console
248
249 .. warning::
250
251     ``_super`` is not a standard method, it is set on-the-fly to the next
252     method in the current inheritance chain, if any. It is only defined
253     during the *synchronous* part of a method call, for use in asynchronous
254     handlers (after network calls or in ``setTimeout`` callbacks) a reference
255     to its value should be retained, it should not be accessed via ``this``::
256
257         // broken, will generate an error
258         say_hello: function () {
259             setTimeout(function () {
260                 this._super();
261             }.bind(this), 0);
262         }
263
264         // correct
265         say_hello: function () {
266             // don't forget .bind()
267             var _super = this._super.bind(this);
268             setTimeout(function () {
269                 _super();
270             }.bind(this), 0);
271         }
272
273 Widgets Basics
274 ==============
275
276 The Odoo web client bundles jQuery_ for easy DOM manipulation. It is useful
277 and provides a better API than standard `W3C DOM`_\ [#dombugs]_, but
278 insufficient to structure complex applications leading to difficult
279 maintenance.
280
281 Much like object-oriented desktop UI toolkits (e.g. Qt_, Cocoa_ or GTK_),
282 Odoo Web makes specific components responsible for sections of a page. In
283 Odoo web, the base for such components is the :class:`~openerp.Widget`
284 class, a component specialized in handling a page section and displaying
285 information for the user.
286
287 Your First Widget
288 -----------------
289
290 The initial demonstration module already provides a basic widget::
291
292     local.HomePage = instance.Widget.extend({
293         start: function() {
294             console.log("pet store home page loaded");
295         },
296     });
297
298 It extends :class:`~openerp.Widget` and overrides the standard method
299 :func:`~openerp.Widget.start`, which â€” much like the previous ``MyClass``
300 — does little for now.
301
302 This line at the end of the file::
303
304     instance.web.client_actions.add(
305         'petstore.homepage', 'instance.oepetstore.HomePage');
306
307 registers our basic widget as a client action. Client actions will be
308 explained later in the guide, for now this is just what allows our widget to
309 be called and displayed when we select the
310 :menuselection:`Pet Store --> Pet Store --> Home Page` menu.
311
312 .. warning::
313
314     because the widget will be called from outside our module, the web client
315     needs its "fully qualified" name, not the local version.
316
317 Display Content
318 ---------------
319
320 Widgets have a number of methods and features, but the basics are simple:
321
322 * set up a widget
323 * format the widget's data
324 * display the widget
325
326 The ``HomePage`` widget already has a :func:`~openerp.Widget.start`
327 method. That method is part of the normal widget lifecycle and automatically
328 called once the widget is inserted in the page. We can use it to display some
329 content.
330
331 All widgets have a :attr:`~openerp.Widget.$el` which represents the
332 section of page they're in charge of (as a jQuery_ object). Widget content
333 should be inserted there. By default, :attr:`~openerp.Widget.$el` is an
334 empty ``<div>`` element.
335
336 A ``<div>`` element is usually invisible for the user if it does not
337 have any content (or specific styles giving it a size) which is why nothing
338 is displayed on the page when ``HomePage`` is launched.
339
340 Let's add some content to the widget's root element, using jQuery::
341
342     local.HomePage = instance.Widget.extend({
343         start: function() {
344             this.$el.append("<div>Hello dear Odoo user!</div>");
345         },
346     });
347
348 That message will now appear when you open :menuselection:`Pet Store
349 --> Pet Store --> Home Page`
350
351 .. note::
352
353     to refresh the javascript code loaded in Odoo Web, you will need to reload
354     the page. There is no need to restart the Odoo server
355
356 The ``HomePage`` widget is used by Odoo Web and managed automatically, to
357 learn how to use a widget "from scratch" let's create a new one::
358
359     local.GreetingsWidget = instance.Widget.extend({
360         start: function() {
361             this.$el.append("<div>We are so happy to see you again in this menu!</div>");
362         },
363     });
364
365 We can now add our ``GreetingsWidget`` to the ``HomePage`` by using the
366 ``GreetingsWidget``'s :func:`~openerp.Widget.appendTo` method::
367
368     local.HomePage = instance.Widget.extend({
369         start: function() {
370             this.$el.append("<div>Hello dear Odoo user!</div>");
371             var greeting = new local.GreetingsWidget(this);
372             return greeting.appendTo(this.$el);
373         },
374     });
375
376 * ``HomePage`` first adds its own content to its DOM root
377 * ``HomePage`` then instantiates ``GreetingsWidget``
378 * Finally it tells ``GreetingsWidget`` where to insert itself, delegating part
379   of its :attr:`~openerp.Widget.$el` to the ``GreetingsWidget``.
380
381 When the :func:`~openerp.Widget.appendTo` method is called, it asks the
382 widget to insert itself at the specified position and to display its content.
383 The :func:`~openerp.Widget.start` method will be called during the call
384 to :func:`~openerp.Widget.appendTo`.
385
386 To see what happens under the displayed interface, we will use the browser's
387 DOM Explorer. But first let's alter our widgets slightly so we can more easily
388 find where they are, by :attr:`adding a class to their root elements
389 <openerp.Widget.className>`::
390
391     local.HomePage = instance.Widget.extend({
392         className: 'oe_petstore_homepage',
393         ...
394     });
395     local.GreetingsWidget = instance.Widget.extend({
396         className: 'oe_petstore_greetings',
397         ...
398     });
399
400 If you can find the relevant section of the DOM (right-click on the text
401 then :guilabel:`Inspect Element`), it should look like this:
402
403 .. code-block:: html
404
405     <div class="oe_petstore_homepage">
406         <div>Hello dear Odoo user!</div>
407         <div class="oe_petstore_greetings">
408             <div>We are so happy to see you again in this menu!</div>
409         </div>
410     </div>
411
412 Which clearly shows the two ``<div>`` elements automatically created by
413 :class:`~openerp.Widget`, because we added some classes on them.
414
415 We can also see the two message-holding divs we added ourselves
416
417 Finally, note the ``<div class="oe_petstore_greetings">`` element which
418 represents the ``GreetingsWidget`` instance is *inside* the
419 ``<div class="oe_petstore_homepage">`` which represents the ``HomePage``
420 instance, since we appended
421
422 Widget Parents and Children
423 ---------------------------
424
425 In the previous part, we instantiated a widget using this syntax::
426
427     new local.GreetingsWidget(this);
428
429 The first argument is ``this``, which in that case was a ``HomePage``
430 instance. This tells the widget being created which other widget is its
431 *parent*.
432
433 As we've seen, widgets are usually inserted in the DOM by another widget and
434 *inside* that other widget's root element. This means most widgets are "part"
435 of another widget, and exist on behalf of it. We call the container the
436 *parent*, and the contained widget the *child*.
437
438 Due to multiple technical and conceptual reasons, it is necessary for a widget
439 to know who is his parent and who are its children.
440
441 :func:`~openerp.Widget.getParent`
442     can be used to get the parent of a widget::
443
444         local.GreetingsWidget = instance.Widget.extend({
445             start: function() {
446                 console.log(this.getParent().$el );
447                 // will print "div.oe_petstore_homepage" in the console
448             },
449         });
450
451 :func:`~openerp.Widget.getChildren`
452     can be used to get a list of its children::
453
454         local.HomePage = instance.Widget.extend({
455             start: function() {
456                 var greeting = new local.GreetingsWidget(this);
457                 greeting.appendTo(this.$el);
458                 console.log(this.getChildren()[0].$el);
459                 // will print "div.oe_petstore_greetings" in the console
460             },
461         });
462
463 When overriding the :func:`~openerp.Widget.init` method of a widget it is
464 *of the utmost importance* to pass the parent to the ``this._super()`` call,
465 otherwise the relation will not be set up correctly::
466
467     local.GreetingsWidget = instance.Widget.extend({
468         init: function(parent, name) {
469             this._super(parent);
470             this.name = name;
471         },
472     });
473
474 Finally, if a widget does not have a parent (e.g. because it's the root
475 widget of the application), ``null`` can be provided as parent::
476
477     new local.GreetingsWidget(null);
478
479 Destroying Widgets
480 ------------------
481
482 If you can display content to your users, you should also be able to erase
483 it. This is done via the :func:`~openerp.Widget.destroy` method::
484
485     greeting.destroy();
486
487 When a widget is destroyed it will first call
488 :func:`~openerp.Widget.destroy` on all its children. Then it erases itself
489 from the DOM. If you have set up permanent structures in
490 :func:`~openerp.Widget.init` or :func:`~openerp.Widget.start` which
491 must be explicitly cleaned up (because the garbage collector will not handle
492 them), you can override :func:`~openerp.Widget.destroy`.
493
494 .. danger::
495
496     when overriding :func:`~openerp.Widget.destroy`, ``_super()``
497     *must always* be called otherwise the widget and its children are not
498     correctly cleaned up leaving possible memory leaks and "phantom events",
499     even if no error is displayed
500
501 The QWeb Template Engine
502 ========================
503
504 In the previous section we added content to our widgets by directly
505 manipulating (and adding to) their DOM::
506
507     this.$el.append("<div>Hello dear Odoo user!</div>");
508
509 This allows generating and displaying any type of content, but tends to
510 rapidly get unwieldy when generating significant amounts of DOM (lots of
511 duplication, quoting issues, ...)
512
513 As many other environments, Odoo's solution is to use a `template engine`_.
514 Odoo's template engine is called :ref:`reference/qweb`.
515
516 QWeb is an XML-based templating language, similar to `Genshi
517 <http://en.wikipedia.org/wiki/Genshi_(templating_language)>`_, `Thymeleaf
518 <http://en.wikipedia.org/wiki/Thymeleaf>`_ or `Facelets
519 <http://en.wikipedia.org/wiki/Facelets>`_. It has the following
520 characteristics:
521
522 * It's implemented fully in JavaScript and rendered in the browser
523 * Each template file (XML files) contains multiple templates
524 * It has special support in Odoo Web's :class:`~openerp.Widget`, though it
525   can be used outside of Odoo's web client (and it's possible to use
526   :class:`~openerp.Widget` without relying on QWeb)
527
528 .. note::
529
530     The rationale behind using QWeb instead of existing javascript template
531     engines is the extensibility of pre-existing (third-party) templates, much
532     like Odoo :ref:`views <reference/views>`.
533
534     Most javascript template engines are text-based which precludes easy
535     structural extensibility where an XML-based templating engine can be
536     generically altered using e.g. XPath or CSS and a tree-alteration DSL (or
537     even just XSLT). This flexibility and extensibility is a core
538     characteristic of Odoo, and losing it was considered unacceptable.
539
540 Using QWeb
541 ----------
542
543 First let's define a simple QWeb template in the almost-empty
544 ``oepetstore/static/src/xml/petstore.xml`` file:
545
546 .. code-block:: xml
547
548     <?xml version="1.0" encoding="UTF-8"?>
549     <templates xml:space="preserve">
550         <t t-name="HomePageTemplate">
551             <div style="background-color: red;">This is some simple HTML</div>
552         </t>
553     </templates>
554
555 Now we can use this template inside of the ``HomePage`` widget. Using the
556 ``QWeb`` loader variable defined at the top of the page, we can call to the
557 template defined in the XML file::
558
559     local.HomePage = instance.Widget.extend({
560         start: function() {
561             this.$el.append(QWeb.render("HomePageTemplate"));
562         },
563     });
564
565 :func:`QWeb.render` looks for the specified template, renders it to a string
566 and returns the result.
567
568 However, because :class:`~openerp.Widget` has special integration for QWeb
569 the template can be set directly on the widget via its
570 :attr:`~openerp.Widget.template` attribute::
571
572     local.HomePage = instance.Widget.extend({
573         template: "HomePageTemplate",
574         start: function() {
575             ...
576         },
577     });
578
579 Although the result look similar, there are two differences between these
580 usages:
581
582 * with the second version, the template is rendered right before
583   :func:`~openerp.Widget.start` is called
584 * in the first version the template's content is added to the widget's root
585   element, whereas in the second version the template's root element is
586   directly *set as* the widget's root element. Which is why the "greetings"
587   sub-widget also gets a red background
588
589 .. warning::
590
591     templates should have a single non-``t`` root element, especially if
592     they're set as a widget's :attr:`~openerp.Widget.template`. If there are
593     multiple "root elements", results are undefined (usually only the first
594     root element will be used and the others will be ignored)
595
596 QWeb Context
597 ''''''''''''
598
599 QWeb templates can be given data and can contain basic display logic.
600
601 For explicit calls to :func:`QWeb.render`, the template data is passed as
602 second parameter::
603
604     QWeb.render("HomePageTemplate", {name: "Klaus"});
605
606 with the template modified to:
607
608 .. code-block:: xml
609
610     <t t-name="HomePageTemplate">
611         <div>Hello <t t-esc="name"/></div>
612     </t>
613
614 will result in:
615
616 .. code-block:: html
617
618     <div>Hello Klaus</div>
619
620 When using :class:`~openerp.Widget`'s integration it is not possible to
621 provide additional data to the template. The template will be given a single
622 ``widget`` context variable, referencing the widget being rendered right
623 before :func:`~openerp.Widget.start` is called (the widget's state will
624 essentially be that set up by :func:`~openerp.Widget.init`):
625
626 .. code-block:: xml
627
628     <t t-name="HomePageTemplate">
629         <div>Hello <t t-esc="widget.name"/></div>
630     </t>
631
632 ::
633
634     local.HomePage = instance.Widget.extend({
635         template: "HomePageTemplate",
636         init: function(parent) {
637             this._super(parent);
638             this.name = "Mordecai";
639         },
640         start: function() {
641         },
642     });
643
644 Result:
645
646 .. code-block:: html
647
648     <div>Hello Mordecai</div>
649
650 Template Declaration
651 ''''''''''''''''''''
652
653 We've seen how to *render* QWeb templates, let's now see the syntax of
654 the templates themselves.
655
656 A QWeb template is composed of regular XML mixed with QWeb *directives*. A
657 QWeb directive is declared with XML attributes starting with ``t-``.
658
659 The most basic directive is ``t-name``, used to declare new templates in
660 a template file:
661
662 .. code-block:: xml
663
664     <templates>
665         <t t-name="HomePageTemplate">
666             <div>This is some simple HTML</div>
667         </t>
668     </templates>
669
670 ``t-name`` takes the name of the template being defined, and declares that
671 it can be called using :func:`QWeb.render`. It can only be used at the
672 top-level of a template file.
673
674 Escaping
675 ''''''''
676
677 The ``t-esc`` directive can be used to output text:
678
679 .. code-block:: xml
680
681     <div>Hello <t t-esc="name"/></div>
682
683 It takes a Javascript expression which is evaluated, the result of the
684 expression is then HTML-escaped and inserted in the document. Since it's an
685 expression it's possible to provide just a variable name as above, or a more
686 complex expression like a computation:
687
688 .. code-block:: xml
689
690     <div><t t-esc="3+5"/></div>
691
692 or method calls:
693
694 .. code-block:: xml
695
696     <div><t t-esc="name.toUpperCase()"/></div>
697
698 Outputting HTML
699 '''''''''''''''
700
701 To inject HTML in the page being rendered, use ``t-raw``. Like ``t-esc`` it
702 takes an arbitrary Javascript expression as parameter, but it does not
703 perform an HTML-escape step.
704
705 .. code-block:: xml
706
707     <div><t t-raw="name.link(user_account)"/></div>
708
709 .. danger::
710
711     ``t-raw`` *must not* be used on any data which may contain non-escaped
712     user-provided content as this leads to `cross-site scripting`_
713     vulnerabilities
714
715 Conditionals
716 ''''''''''''
717
718 QWeb can have conditional blocks using ``t-if``. The directive takes an
719 arbitrary expression, if the expression is falsy (``false``, ``null``, ``0``
720 or an empty string) the whole block is suppressed, otherwise it is displayed.
721
722 .. code-block:: xml
723
724     <div>
725         <t t-if="true == true">
726             true is true
727         </t>
728         <t t-if="true == false">
729             true is not true
730         </t>
731     </div>
732
733 .. note::
734
735     QWeb doesn't have an "else" structure, use a second ``t-if`` with the
736     original condition inverted. You may want to store the condition in a
737     local variable if it's a complex or expensive expression.
738
739 Iteration
740 '''''''''
741
742 To iterate on a list, use ``t-foreach`` and ``t-as``. ``t-foreach`` takes an
743 expression returning a list to iterate on ``t-as`` takes a variable name to
744 bind to each item during iteration.
745
746 .. code-block:: xml
747
748     <div>
749         <t t-foreach="names" t-as="name">
750             <div>
751                 Hello <t t-esc="name"/>
752             </div>
753         </t>
754     </div>
755
756 .. note:: ``t-foreach`` can also be used with numbers and objects
757           (dictionaries)
758
759 Defining attributes
760 '''''''''''''''''''
761
762 QWeb provides two related directives to define computed attributes:
763 :samp:`t-att-{name}` and :samp:`t-attf-{name}`. In either case, *name* is the
764 name of the attribute to create (e.g. ``t-att-id`` defines the attribute
765 ``id`` after rendering).
766
767 ``t-att-`` takes a javascript expression whose result is set as the
768 attribute's value, it is most useful if all of the attribute's value is
769 computed:
770
771 .. code-block:: xml
772
773     <div>
774         Input your name:
775         <input type="text" t-att-value="defaultName"/>
776     </div>
777
778 ``t-attf-`` takes a *format string*. A format string is literal text with
779 interpolation blocks inside, an interpolation block is a javascript
780 expression between ``{{`` and ``}}``, which will be replaced by the result
781 of the expression. It is most useful for attributes which are partially
782 literal and partially computed such as a class:
783
784 .. code-block:: xml
785
786     <div t-attf-class="container {{ left ? 'text-left' : '' }} {{ extra_class }}">
787         insert content here
788     </div>
789
790 Calling other templates
791 '''''''''''''''''''''''
792
793 Templates can be split into sub-templates (for simplicity, maintainability,
794 reusability or to avoid excessive markup nesting).
795
796 This is done using the ``t-call`` directive, which takes the name of the
797 template to render:
798
799 .. code-block:: xml
800
801     <t t-name="A">
802         <div class="i-am-a">
803             <t t-call="B"/>
804         </div>
805     </t>
806     <t t-name="B">
807         <div class="i-am-b"/>
808     </t>
809
810 rendering the ``A`` template will result in:
811
812 .. code-block:: xml
813
814     <div class="i-am-a">
815         <div class="i-am-b"/>
816     </div>
817
818 Sub-templates inherit the rendering context of their caller.
819
820 To Learn More About QWeb
821 ''''''''''''''''''''''''
822
823 For a QWeb reference, see :ref:`reference/qweb`.
824
825 Exercise
826 ''''''''
827
828 .. exercise:: Usage of QWeb in Widgets
829
830     Create a widget whose constructor takes two parameters aside from
831     ``parent``: ``product_names`` and ``color``.
832
833     * ``product_names`` should an array of strings, each one the name of a
834       product
835     * ``color`` is a string containing a color in CSS color format (ie:
836       ``#000000`` for black).
837
838     The widget should display the given product names one under the other,
839     each one in a separate box with a background color with the value of
840     ``color`` and a border. You should use QWeb to render the HTML. Any
841     necessary CSS should be in ``oepetstore/static/src/css/petstore.css``.
842
843     Use the widget in ``HomePage`` with half a dozen products.
844
845     .. only:: solutions
846
847         ::
848
849             openerp.oepetstore = function(instance, local) {
850                 var _t = instance.web._t,
851                     _lt = instance.web._lt;
852                 var QWeb = instance.web.qweb;
853
854                 local.HomePage = instance.Widget.extend({
855                     start: function() {
856                         var products = new local.ProductsWidget(
857                             this, ["cpu", "mouse", "keyboard", "graphic card", "screen"], "#00FF00");
858                         products.appendTo(this.$el);
859                     },
860                 });
861
862                 local.ProductsWidget = instance.Widget.extend({
863                     template: "ProductsWidget",
864                     init: function(parent, products, color) {
865                         this._super(parent);
866                         this.products = products;
867                         this.color = color;
868                     },
869                 });
870
871                 instance.web.client_actions.add(
872                     'petstore.homepage', 'instance.oepetstore.HomePage');
873             }
874
875         .. code-block:: xml
876
877             <?xml version="1.0" encoding="UTF-8"?>
878             <templates xml:space="preserve">
879                 <t t-name="ProductsWidget">
880                     <div>
881                         <t t-foreach="widget.products" t-as="product">
882                             <span class="oe_products_item"
883                                   t-attf-style="background-color: {{ widget.color }};">
884                                 <t t-esc="product"/>
885                             </span>
886                             <br/>
887                         </t>
888                     </div>
889                 </t>
890             </templates>
891
892         .. code-block:: css
893
894             .oe_products_item {
895                 display: inline-block;
896                 padding: 3px;
897                 margin: 5px;
898                 border: 1px solid black;
899                 border-radius: 3px;
900             }
901
902         .. image:: web/qweb.*
903            :align: center
904            :width: 70%
905
906 Widget Helpers
907 ==============
908
909 ``Widget``'s jQuery Selector
910 ----------------------------
911
912 Selecting DOM elements within a widget can be performed by calling the
913 ``find()`` method on the widget's DOM root::
914
915     this.$el.find("input.my_input")...
916
917 But because it's an extremely common operation, :class:`~openerp.Widget`
918 provides an equivalent shortcut through the :func:`~openerp.Widget.$`
919 method::
920
921     local.MyWidget = instance.Widget.extend({
922         start: function() {
923             this.$("input.my_input")...
924         },
925     });
926
927 .. warning::
928
929     The global jQuery function ``$()`` should *never* be used unless it is
930     absolutely necessary: selection on a widget's root are scoped to the
931     widget and local to it, but selections with ``$()`` are global to the
932     page/application and may match parts of other widgets and views, leading
933     to odd or dangerous side-effects. Since a widget should generally act
934     only on the DOM section it owns, there is no cause for global selection.
935
936 Easier DOM Events Binding
937 -------------------------
938
939 We have previously bound DOM events using normal jQuery event handlers (e.g.
940 ``.click()`` or ``.change()``) on widget elements::
941
942     local.MyWidget = instance.Widget.extend({
943         start: function() {
944             var self = this;
945             this.$(".my_button").click(function() {
946                 self.button_clicked();
947             });
948         },
949         button_clicked: function() {
950             ..
951         },
952     });
953
954 While this works it has a few issues:
955
956 1. it is rather verbose
957 2. it does not support replacing the widget's root element at runtime as
958    the binding is only performed when ``start()`` is run (during widget
959    initialization)
960 3. it requires dealing with ``this``-binding issues
961
962 Widgets thus provide a shortcut to DOM event binding via
963 :attr:`~openerp.Widget.events`::
964
965     local.MyWidget = instance.Widget.extend({
966         events: {
967             "click .my_button": "button_clicked",
968         },
969         button_clicked: function() {
970             ..
971         }
972     });
973
974 :attr:`~openerp.Widget.events` is an object (mapping) of an event to the
975 function or method to call when the event is triggered:
976
977 * the key is an event name, possibly refined with a CSS selector in which
978   case only if the event happens on a selected sub-element will the function
979   or method run: ``click`` will handle all clicks within the widget, but
980   ``click .my_button`` will only handle clicks in elements bearing the
981   ``my_button`` class
982 * the value is the action to perform when the event is triggered
983
984   It can be either a function::
985
986       events: {
987           'click': function (e) { /* code here */ }
988       }
989
990   or the name of a method on the object (see example above).
991
992   In either case, the ``this`` is the widget instance and the handler is given
993   a single parameter, the `jQuery event object`_ for the event.
994
995 Widget Events and Properties
996 ============================
997
998 Events
999 ------
1000
1001 Widgets provide an event system (separate from the DOM/jQuery event system
1002 described above): a widget can fire events on itself, and other widgets (or
1003 itself) can bind themselves and listen for these events::
1004
1005     local.ConfirmWidget = instance.Widget.extend({
1006         events: {
1007             'click button.ok_button': function () {
1008                 this.trigger('user_chose', true);
1009             },
1010             'click button.cancel_button': function () {
1011                 this.trigger('user_chose', false);
1012             }
1013         },
1014         start: function() {
1015             this.$el.append("<div>Are you sure you want to perform this action?</div>" +
1016                 "<button class='ok_button'>Ok</button>" +
1017                 "<button class='cancel_button'>Cancel</button>");
1018         },
1019     });
1020
1021 This widget acts as a facade, transforming user input (through DOM events)
1022 into a documentable internal event to which parent widgets can bind
1023 themselves.
1024
1025 :func:`~openerp.Widget.trigger` takes the name of the event to trigger as
1026 its first (mandatory) argument, any further arguments are treated as event
1027 data and passed directly to listeners.
1028
1029 We can then set up a parent event instantiating our generic widget and
1030 listening to the ``user_chose`` event using :func:`~openerp.Widget.on`::
1031
1032     local.HomePage = instance.Widget.extend({
1033         start: function() {
1034             var widget = new local.ConfirmWidget(this);
1035             widget.on("user_chose", this, this.user_chose);
1036             widget.appendTo(this.$el);
1037         },
1038         user_chose: function(confirm) {
1039             if (confirm) {
1040                 console.log("The user agreed to continue");
1041             } else {
1042                 console.log("The user refused to continue");
1043             }
1044         },
1045     });
1046
1047 :func:`~openerp.Widget.on` binds a function to be called when the
1048 event identified by ``event_name`` is. The ``func`` argument is the
1049 function to call and ``object`` is the object to which that function is
1050 related if it is a method. The bound function will be called with the
1051 additional arguments of :func:`~openerp.Widget.trigger` if it has
1052 any. Example::
1053
1054     start: function() {
1055         var widget = ...
1056         widget.on("my_event", this, this.my_event_triggered);
1057         widget.trigger("my_event", 1, 2, 3);
1058     },
1059     my_event_triggered: function(a, b, c) {
1060         console.log(a, b, c);
1061         // will print "1 2 3"
1062     }
1063
1064 .. note::
1065
1066     Triggering events on an other widget is generally a bad idea. The main
1067     exception to that rule is ``openerp.web.bus`` which exists specifically
1068     to broadcasts evens in which any widget could be interested throughout
1069     the Odoo web application.
1070
1071 Properties
1072 ----------
1073
1074 Properties are very similar to normal object attributes in that they allow
1075 storing data on a widget instance, however they have the additional feature
1076 that they trigger events when set::
1077
1078     start: function() {
1079         this.widget = ...
1080         this.widget.on("change:name", this, this.name_changed);
1081         this.widget.set("name", "Nicolas");
1082     },
1083     name_changed: function() {
1084         console.log("The new value of the property 'name' is", this.widget.get("name"));
1085     }
1086
1087 * :func:`~openerp.Widget.set` sets the value of a property and triggers
1088   :samp:`change:{propname}` (where *propname* is the property name passed as
1089   first parameter to :func:`~openerp.Widget.set`) and ``change``
1090 * :func:`~openerp.Widget.get` retrieves the value of a property.
1091
1092 Exercise
1093 --------
1094
1095 .. exercise:: Widget Properties and Events
1096
1097     Create a widget ``ColorInputWidget`` that will display 3 ``<input
1098     type="text">``. Each of these ``<input>`` is dedicated to type a
1099     hexadecimal number from 00 to FF. When any of these ``<input>`` is
1100     modified by the user the widget must query the content of the three
1101     ``<input>``, concatenate their values to have a complete CSS color code
1102     (ie: ``#00FF00``) and put the result in a property named ``color``. Please
1103     note the jQuery ``change()`` event that you can bind on any HTML
1104     ``<input>`` element and the ``val()`` method that can query the current
1105     value of that ``<input>`` could be useful to you for this exercise.
1106
1107     Then, modify the ``HomePage`` widget to instantiate ``ColorInputWidget``
1108     and display it. The ``HomePage`` widget should also display an empty
1109     rectangle. That rectangle must always, at any moment, have the same
1110     background color than the color in the ``color`` property of the
1111     ``ColorInputWidget`` instance.
1112
1113     Use QWeb to generate all HTML.
1114
1115     .. only:: solutions
1116
1117         ::
1118
1119             openerp.oepetstore = function(instance, local) {
1120                 var _t = instance.web._t,
1121                     _lt = instance.web._lt;
1122                 var QWeb = instance.web.qweb;
1123
1124                 local.ColorInputWidget = instance.Widget.extend({
1125                     template: "ColorInputWidget",
1126                     events: {
1127                         'change input': 'input_changed'
1128                     },
1129                     start: function() {
1130                         this.input_changed();
1131                         return this._super();
1132                     },
1133                     input_changed: function() {
1134                         var color = [
1135                             "#",
1136                             this.$(".oe_color_red").val(),
1137                             this.$(".oe_color_green").val(),
1138                             this.$(".oe_color_blue").val()
1139                         ].join('');
1140                         this.set("color", color);
1141                     },
1142                 });
1143
1144                 local.HomePage = instance.Widget.extend({
1145                     template: "HomePage",
1146                     start: function() {
1147                         this.colorInput = new local.ColorInputWidget(this);
1148                         this.colorInput.on("change:color", this, this.color_changed);
1149                         return this.colorInput.appendTo(this.$el);
1150                     },
1151                     color_changed: function() {
1152                         this.$(".oe_color_div").css("background-color", this.colorInput.get("color"));
1153                     },
1154                 });
1155
1156                 instance.web.client_actions.add('petstore.homepage', 'instance.oepetstore.HomePage');
1157             }
1158
1159         .. code-block:: xml
1160
1161             <?xml version="1.0" encoding="UTF-8"?>
1162             <templates xml:space="preserve">
1163                 <t t-name="ColorInputWidget">
1164                     <div>
1165                         Red: <input type="text" class="oe_color_red" value="00"></input><br />
1166                         Green: <input type="text" class="oe_color_green" value="00"></input><br />
1167                         Blue: <input type="text" class="oe_color_blue" value="00"></input><br />
1168                     </div>
1169                 </t>
1170                 <t t-name="HomePage">
1171                     <div>
1172                         <div class="oe_color_div"></div>
1173                     </div>
1174                 </t>
1175             </templates>
1176
1177         .. code-block:: css
1178
1179             .oe_color_div {
1180                 width: 100px;
1181                 height: 100px;
1182                 margin: 10px;
1183             }
1184
1185 Modify existing widgets and classes
1186 ===================================
1187
1188 The class system of the Odoo web framework allows direct modification of
1189 existing classes using the :func:`~openerp.web.Class.include` method::
1190
1191     var TestClass = instance.web.Class.extend({
1192         testMethod: function() {
1193             return "hello";
1194         },
1195     });
1196
1197     TestClass.include({
1198         testMethod: function() {
1199             return this._super() + " world";
1200         },
1201     });
1202
1203     console.log(new TestClass().testMethod());
1204     // will print "hello world"
1205
1206 This system is similar to the inheritance mechanism, except it will alter the
1207 target class in-place instead of creating a new class.
1208
1209 In that case, ``this._super()`` will call the original implementation of a
1210 method being replaced/redefined. If the class already had sub-classes, all
1211 calls to ``this._super()`` in sub-classes will call the new implementations
1212 defined in the call to :func:`~openerp.web.Class.include`. This will also work
1213 if some instances of the class (or of any of its sub-classes) were created
1214 prior to the call to :func:`~openerp.Widget.include`.
1215
1216 Translations
1217 ============
1218
1219 The process to translate text in Python and JavaScript code is very
1220 similar. You could have noticed these lines at the beginning of the
1221 ``petstore.js`` file::
1222
1223     var _t = instance.web._t,
1224         _lt = instance.web._lt;
1225
1226 These lines are simply used to import the translation functions in the current
1227 JavaScript module. They are used thus::
1228
1229     this.$el.text(_t("Hello user!"));
1230
1231 In Odoo, translations files are automatically generated by scanning the source
1232 code. All piece of code that calls a certain function are detected and their
1233 content is added to a translation file that will then be sent to the
1234 translators. In Python, the function is ``_()``. In JavaScript the function is
1235 :func:`~openerp.web._t` (and also :func:`~openerp.web._lt`).
1236
1237 ``_t()`` will return the translation defined for the text it is given. If no
1238 translation is defined for that text, it will return the original text as-is.
1239
1240 .. note::
1241
1242     To inject user-provided values in translatable strings, it is recommended
1243     to use `_.str.sprintf
1244     <http://gabceb.github.io/underscore.string.site/#sprintf>`_ with named
1245     arguments *after* the translation::
1246
1247         this.$el.text(_.str.sprintf(
1248             _t("Hello, %(user)s!"), {
1249             user: "Ed"
1250         }));
1251
1252     This makes translatable strings more readable to translators, and gives
1253     them more flexibility to reorder or ignore parameters.
1254
1255 :func:`~openerp.web._lt` ("lazy translate") is similar but somewhat more
1256 complex: instead of translating its parameter immediately, it returns
1257 an object which, when converted to a string, will perform the translation.
1258
1259 It is used to define translatable terms before the translations system is
1260 initialized, for class attributes for instance (as modules are loaded before
1261 the user's language is configured and translations are downloaded).
1262
1263 Communication with the Odoo Server
1264 ==================================
1265
1266 Contacting Models
1267 -----------------
1268
1269 Most operations with Odoo involve communicating with *models* implementing
1270 business concern, these models will then (potentially) interact with some
1271 storage engine (usually PostgreSQL_).
1272
1273 Although jQuery_ provides a `$.ajax`_ function for network interactions,
1274 communicating with Odoo requires additional metadata whose setup before every
1275 call would be verbose and error-prone. As a result, Odoo web provides
1276 higher-level communication primitives.
1277
1278 To demonstrate this, the file ``petstore.py`` already contains a small model
1279 with a sample method:
1280
1281 .. code-block:: python
1282
1283     class message_of_the_day(models.Model):
1284         _name = "oepetstore.message_of_the_day"
1285
1286         @api.model
1287         def my_method(self):
1288             return {"hello": "world"}
1289
1290         message = fields.Text(),
1291         color = fields.Char(size=20),
1292
1293 This declares a model with two fields, and a method ``my_method()`` which
1294 returns a literal dictionary.
1295
1296 Here is a sample widget that calls ``my_method()`` and displays the result::
1297
1298     local.HomePage = instance.Widget.extend({
1299         start: function() {
1300             var self = this;
1301             var model = new instance.web.Model("oepetstore.message_of_the_day");
1302             model.call("my_method", {context: new instance.web.CompoundContext()}).then(function(result) {
1303                 self.$el.append("<div>Hello " + result["hello"] + "</div>");
1304                 // will show "Hello world" to the user
1305             });
1306         },
1307     });
1308
1309 The class used to call Odoo models is :class:`openerp.Model`. It is
1310 instantiated with the Odoo model's name as first parameter
1311 (``oepetstore.message_of_the_day`` here).
1312
1313 :func:`~openerp.web.Model.call` can be used to call any (public) method of an
1314 Odoo model. It takes the following positional arguments:
1315
1316 ``name``
1317   The name of the method to call, ``my_method`` here
1318 ``args``
1319   an array of `positional arguments`_ to provide to the method. Because the
1320   example has no positional argument to provide, the ``args`` parameter is not
1321   provided.
1322
1323   Here is an other example with positional arguments:
1324
1325   .. code-block:: python
1326
1327       @api.model
1328       def my_method2(self, a, b, c): ...
1329
1330   .. code-block:: javascript
1331
1332       model.call("my_method", [1, 2, 3], ...
1333       // with this a=1, b=2 and c=3
1334
1335 ``kwargs``
1336   a mapping of `keyword arguments`_ to pass. The example provides a single
1337   named argument ``context``.
1338
1339   .. code-block:: python
1340
1341       @api.model
1342       def my_method2(self, a, b, c): ...
1343
1344   .. code-block:: javascript
1345
1346       model.call("my_method", [], {a: 1, b: 2, c: 3, ...
1347       // with this a=1, b=2 and c=3
1348
1349 :func:`~openerp.Widget.call` returns a deferred resolved with the value
1350 returned by the model's method as first argument.
1351
1352 CompoundContext
1353 ---------------
1354
1355 The previous section used a ``context`` argument which was not explained in
1356 the method call::
1357
1358     model.call("my_method", {context: new instance.web.CompoundContext()})
1359
1360 The context is like a "magic" argument that the web client will always give to
1361 the server when calling a method. The context is a dictionary containing
1362 multiple keys. One of the most important key is the language of the user, used
1363 by the server to translate all the messages of the application. Another one is
1364 the time zone of the user, used to compute correctly dates and times if Odoo
1365 is used by people in different countries.
1366
1367 The ``argument`` is necessary in all methods, because if we forget it bad
1368 things could happen (like the application not being translated
1369 correctly). That's why, when you call a model's method, you should always give
1370 it to that argument. The solution to achieve that is to use
1371 :class:`openerp.web.CompoundContext`.
1372
1373 :class:`~openerp.web.CompoundContext` is a class used to pass the user's
1374 context (with language, time zone, etc...) to the server as well as adding new
1375 keys to the context (some models' methods use arbitrary keys added to the
1376 context). It is created by giving to its constructor any number of
1377 dictionaries or other :class:`~openerp.web.CompoundContext` instances. It will
1378 merge all those contexts before sending them to the server.
1379
1380 .. code-block:: javascript
1381
1382     model.call("my_method", {context: new instance.web.CompoundContext({'new_key': 'key_value'})})
1383
1384 .. code-block:: python
1385
1386     @api.model
1387     def my_method(self):
1388         print self.env.context
1389         // will print: {'lang': 'en_US', 'new_key': 'key_value', 'tz': 'Europe/Brussels', 'uid': 1}
1390
1391 You can see the dictionary in the argument ``context`` contains some keys that
1392 are related to the configuration of the current user in Odoo plus the
1393 ``new_key`` key that was added when instantiating
1394 :class:`~openerp.web.CompoundContext`.
1395
1396 Queries
1397 -------
1398
1399 While :func:`~openerp.Model.call` is sufficient for any interaction with Odoo
1400 models, Odoo Web provides a helper for simpler and clearer querying of models
1401 (fetching of records based on various conditions):
1402 :func:`~openerp.Model.query` which acts as a shortcut for the common
1403 combination of :py:meth:`~openerp.models.Model.search` and
1404 ::py:meth:`~openerp.models.Model.read`. It provides a clearer syntax to search
1405 and read models::
1406
1407     model.query(['name', 'login', 'user_email', 'signature'])
1408          .filter([['active', '=', true], ['company_id', '=', main_company]])
1409          .limit(15)
1410          .all().then(function (users) {
1411         // do work with users records
1412     });
1413
1414 versus::
1415
1416     model.call('search', [['active', '=', true], ['company_id', '=', main_company]], {limit: 15})
1417         .then(function (ids) {
1418             return model.call('read', [ids, ['name', 'login', 'user_email', 'signature']]);
1419         })
1420         .then(function (users) {
1421             // do work with users records
1422         });
1423
1424 * :func:`~openerp.web.Model.query` takes an optional list of fields as
1425   parameter (if no field is provided, all fields of the model are fetched). It
1426   returns a :class:`openerp.web.Query` which can be further customized before
1427   being executed
1428 * :class:`~openerp.web.Query` represents the query being built. It is
1429   immutable, methods to customize the query actually return a modified copy,
1430   so it's possible to use the original and the new version side-by-side. See
1431   :class:`~openerp.web.Query` for its customization options.
1432
1433 When the query is set up as desired, simply call
1434 :func:`~openerp.web.Query.all` to perform the actual query and return a
1435 deferred to its result. The result is the same as
1436 :py:meth:`~openerp.models.Model.read`'s, an array of dictionaries where each
1437 dictionary is a requested record, with each requested field a dictionary key.
1438
1439 Exercises
1440 =========
1441
1442 .. exercise:: Message of the Day
1443
1444     Create a ``MessageOfTheDay``  widget displaying the last record of the
1445     ``oepetstore.message_of_the_day`` model. The widget should fetch its
1446     record as soon as it is displayed.
1447
1448     Display the widget in the Pet Store home page.
1449
1450     .. only:: solutions
1451
1452         .. code-block:: javascript
1453
1454             openerp.oepetstore = function(instance, local) {
1455                 var _t = instance.web._t,
1456                     _lt = instance.web._lt;
1457                 var QWeb = instance.web.qweb;
1458
1459                 local.HomePage = instance.Widget.extend({
1460                     template: "HomePage",
1461                     start: function() {
1462                         return new local.MessageOfTheDay(this).appendTo(this.$el);
1463                     },
1464                 });
1465
1466                 instance.web.client_actions.add('petstore.homepage', 'instance.oepetstore.HomePage');
1467
1468                 local.MessageOfTheDay = instance.Widget.extend({
1469                     template: "MessageOfTheDay",
1470                     start: function() {
1471                         var self = this;
1472                         return new instance.web.Model("oepetstore.message_of_the_day")
1473                             .query(["message"])
1474                             .order_by('-create_date', '-id')
1475                             .first()
1476                             .then(function(result) {
1477                                 self.$(".oe_mywidget_message_of_the_day").text(result.message);
1478                             });
1479                     },
1480                 });
1481
1482             }
1483
1484         .. code-block:: xml
1485
1486             <?xml version="1.0" encoding="UTF-8"?>
1487             <templates xml:space="preserve">
1488                 <t t-name="HomePage">
1489                     <div class="oe_petstore_homepage">
1490                     </div>
1491                 </t>
1492                 <t t-name="MessageOfTheDay">
1493                     <div class="oe_petstore_motd">
1494                         <p class="oe_mywidget_message_of_the_day"></p>
1495                     </div>
1496                 </t>
1497             </templates>
1498
1499         .. code-block:: css
1500
1501             .oe_petstore_motd {
1502                 margin: 5px;
1503                 padding: 5px;
1504                 border-radius: 3px;
1505                 background-color: #F0EEEE;
1506             }
1507
1508 .. exercise:: Pet Toys List
1509
1510     Create a ``PetToysList`` widget displaying 5 toys (using their name and
1511     their images).
1512
1513     The pet toys are not stored in a new model, instead they're stored in
1514     ``product.product`` using a special category *Pet Toys*. You can see the
1515     pre-generated toys and add new ones by going to
1516     :menuselection:`Pet Store --> Pet Store --> Pet Toys`. You will probably
1517     need to explore ``product.product`` in order to create the right domain to
1518     select just pet toys.
1519
1520     In Odoo, images are generally stored in regular fields encoded as
1521     base64_, HTML supports displaying images straight from base64 with
1522     :samp:`<img src="data:{mime_type};base64,{base64_image_data}"/>`
1523
1524     The ``PetToysList`` widget should be displayed on the home page on the
1525     right of the ``MessageOfTheDay`` widget. You will need to make some layout
1526     with CSS to achieve this.
1527
1528     .. only:: solutions
1529
1530         .. code-block:: javascript
1531
1532             openerp.oepetstore = function(instance, local) {
1533                 var _t = instance.web._t,
1534                     _lt = instance.web._lt;
1535                 var QWeb = instance.web.qweb;
1536
1537                 local.HomePage = instance.Widget.extend({
1538                     template: "HomePage",
1539                     start: function () {
1540                         return $.when(
1541                             new local.PetToysList(this).appendTo(this.$('.oe_petstore_homepage_left')),
1542                             new local.MessageOfTheDay(this).appendTo(this.$('.oe_petstore_homepage_right'))
1543                         );
1544                     }
1545                 });
1546                 instance.web.client_actions.add('petstore.homepage', 'instance.oepetstore.HomePage');
1547
1548                 local.MessageOfTheDay = instance.Widget.extend({
1549                     template: 'MessageOfTheDay',
1550                     start: function () {
1551                         var self = this;
1552                         return new instance.web.Model('oepetstore.message_of_the_day')
1553                             .query(["message"])
1554                             .order_by('-create_date', '-id')
1555                             .first()
1556                             .then(function (result) {
1557                                 self.$(".oe_mywidget_message_of_the_day").text(result.message);
1558                             });
1559                     }
1560                 });
1561
1562                 local.PetToysList = instance.Widget.extend({
1563                     template: 'PetToysList',
1564                     start: function () {
1565                         var self = this;
1566                         return new instance.web.Model('product.product')
1567                             .query(['name', 'image'])
1568                             .filter([['categ_id.name', '=', "Pet Toys"]])
1569                             .limit(5)
1570                             .all()
1571                             .then(function (results) {
1572                                 _(results).each(function (item) {
1573                                     self.$el.append(QWeb.render('PetToy', {item: item}));
1574                                 });
1575                             });
1576                     }
1577                 });
1578             }
1579
1580         .. code-block:: xml
1581
1582             <?xml version="1.0" encoding="UTF-8"?>
1583
1584             <templates xml:space="preserve">
1585                 <t t-name="HomePage">
1586                     <div class="oe_petstore_homepage">
1587                         <div class="oe_petstore_homepage_left"></div>
1588                         <div class="oe_petstore_homepage_right"></div>
1589                     </div>
1590                 </t>
1591                 <t t-name="MessageOfTheDay">
1592                     <div class="oe_petstore_motd">
1593                         <p class="oe_mywidget_message_of_the_day"></p>
1594                     </div>
1595                 </t>
1596                 <t t-name="PetToysList">
1597                     <div class="oe_petstore_pettoyslist">
1598                     </div>
1599                 </t>
1600                 <t t-name="PetToy">
1601                     <div class="oe_petstore_pettoy">
1602                         <p><t t-esc="item.name"/></p>
1603                         <p><img t-att-src="'data:image/jpg;base64,'+item.image"/></p>
1604                     </div>
1605                 </t>
1606             </templates>
1607
1608         .. code-block:: css
1609
1610             .oe_petstore_homepage {
1611                 display: table;
1612             }
1613
1614             .oe_petstore_homepage_left {
1615                 display: table-cell;
1616                 width : 300px;
1617             }
1618
1619             .oe_petstore_homepage_right {
1620                 display: table-cell;
1621                 width : 300px;
1622             }
1623
1624             .oe_petstore_motd {
1625                 margin: 5px;
1626                 padding: 5px;
1627                 border-radius: 3px;
1628                 background-color: #F0EEEE;
1629             }
1630
1631             .oe_petstore_pettoyslist {
1632                 padding: 5px;
1633             }
1634
1635             .oe_petstore_pettoy {
1636                 margin: 5px;
1637                 padding: 5px;
1638                 border-radius: 3px;
1639                 background-color: #F0EEEE;
1640             }
1641
1642
1643 Existing web components
1644 =======================
1645
1646 The Action Manager
1647 ------------------
1648
1649 In Odoo, many operations start from an :ref:`action <reference/actions>`:
1650 opening a menu item (to a view), printing a report, ...
1651
1652 Actions are pieces of data describing how a client should react to the
1653 activation of a piece of content. Actions can be stored (and read through a
1654 model) or they can be generated on-the fly (locally to the client by
1655 javascript code, or remotely by a method of a model).
1656
1657 In Odoo Web, the component responsible for handling and reacting to these
1658 actions is the *Action Manager*.
1659
1660 Using the Action Manager
1661 ''''''''''''''''''''''''
1662
1663 The action manager can be invoked explicitly from javascript code by creating
1664 a dictionary describing :ref:`an action <reference/actions>` of the right
1665 type, and calling an action manager instance with it.
1666
1667 :func:`~openerp.Widget.do_action` is a shortcut of :class:`~openerp.Widget`
1668 looking up the "current" action manager and executing the action::
1669
1670     instance.web.TestWidget = instance.Widget.extend({
1671         dispatch_to_new_action: function() {
1672             this.do_action({
1673                 type: 'ir.actions.act_window',
1674                 res_model: "product.product",
1675                 res_id: 1,
1676                 views: [[false, 'form']],
1677                 target: 'current',
1678                 context: {},
1679             });
1680         },
1681     });
1682
1683 The most common action ``type`` is ``ir.actions.act_window`` which provides
1684 views to a model (displays a model in various manners), its most common
1685 attributes are:
1686
1687 ``res_model``
1688   The model to display in views
1689 ``res_id`` (optional)
1690   For form views, a preselected record in ``res_model``
1691 ``views``
1692   Lists the views available through the action. A list of
1693   ``[view_id, view_type]``, ``view_id`` can either be the database identifier
1694   of a view of the right type, or ``false`` to use the view by default for
1695   the specified type. View types can not be present multiple times. The action
1696   will open the first view of the list by default.
1697 ``target``
1698   Either ``current`` (the default) which replaces the "content" section of the
1699   web client by the action, or ``new`` to open the action in a dialog box.
1700 ``context``
1701   Additional context data to use within the action.
1702
1703 .. exercise:: Jump to Product
1704
1705     Modify the ``PetToysList`` component so clicking on a toy replaces the
1706     homepage by the toy's form view.
1707
1708     .. only:: solutions
1709
1710         .. code-block:: javascript
1711
1712             local.PetToysList = instance.Widget.extend({
1713                 template: 'PetToysList',
1714                 events: {
1715                     'click .oe_petstore_pettoy': 'selected_item',
1716                 },
1717                 start: function () {
1718                     var self = this;
1719                     return new instance.web.Model('product.product')
1720                         .query(['name', 'image'])
1721                         .filter([['categ_id.name', '=', "Pet Toys"]])
1722                         .limit(5)
1723                         .all()
1724                         .then(function (results) {
1725                             _(results).each(function (item) {
1726                                 self.$el.append(QWeb.render('PetToy', {item: item}));
1727                             });
1728                         });
1729                 },
1730                 selected_item: function (event) {
1731                     this.do_action({
1732                         type: 'ir.actions.act_window',
1733                         res_model: 'product.product',
1734                         res_id: $(event.currentTarget).data('id'),
1735                         views: [[false, 'form']],
1736                     });
1737                 },
1738             });
1739
1740         .. code-block:: xml
1741
1742             <t t-name="PetToy">
1743                 <div class="oe_petstore_pettoy" t-att-data-id="item.id">
1744                     <p><t t-esc="item.name"/></p>
1745                     <p><img t-attf-src="data:image/jpg;base64,#{item.image}"/></p>
1746                 </div>
1747             </t>
1748
1749 Client Actions
1750 --------------
1751
1752 Throughout this guide, we used a simple ``HomePage`` widget which the web
1753 client automatically starts when we select the right menu item. But how did
1754 the Odoo web know to start this widget? Because the widget is registered as
1755 a *client action*.
1756
1757 A client action is (as its name implies) an action type defined almost
1758 entirely in the client, in javascript for Odoo web. The server simply sends
1759 an action tag (an arbitrary name), and optionally adds a few parameters, but
1760 beyond that *everything* is handled by custom client code.
1761
1762 Our widget is registered as the handler for the client action through this::
1763
1764     instance.web.client_actions.add('petstore.homepage', 'instance.oepetstore.HomePage');
1765
1766
1767 ``instance.web.client_actions`` is a :class:`~openerp.web.Registry` in which
1768 the action manager looks up client action handlers when it needs to execute
1769 one. The first parameter of :class:`~openerp.web.Registry.add` is the name
1770 (tag) of the client action, and the second parameter is the path to the widget
1771 from the Odoo web client root.
1772
1773 When a client action must be executed, the action manager looks up its tag
1774 in the registry, walks the specified path and displays the widget it finds at
1775 the end.
1776
1777 .. note:: a client action handler can also be a regular function, in whch case
1778           it'll be called and its result (if any) will be interpreted as the
1779           next action to execute.
1780
1781 On the server side, we had simply defined an ``ir.actions.client`` action:
1782
1783 .. code-block:: xml
1784
1785     <record id="action_home_page" model="ir.actions.client">
1786         <field name="tag">petstore.homepage</field>
1787     </record>
1788
1789 and a menu opening the action:
1790
1791 .. code-block:: xml
1792
1793     <menuitem id="home_page_petstore_menu" parent="petstore_menu"
1794               name="Home Page" action="action_home_page"/>
1795
1796 Architecture of the Views
1797 -------------------------
1798
1799 Much of Odoo web's usefulness (and complexity) resides in views. Each view
1800 type is a way of displaying a model in the client.
1801
1802 The View Manager
1803 ''''''''''''''''
1804
1805 When an ``ActionManager`` instance receive an action of type
1806 ``ir.actions.act_window``, it delegates the synchronization and handling of
1807 the views themselves to a *view manager*, which will then set up one or
1808 multiple views depending on the original action's requirements:
1809
1810 .. image:: web/viewarchitecture.*
1811    :align: center
1812    :width: 40%
1813
1814 The Views
1815 '''''''''
1816
1817 Most :ref:`Odoo views <reference/views>` are implemented through a subclass
1818 of :class:`openerp.web.View` which provides a bit of generic basic structure
1819 for handling events and displaying model information.
1820
1821 The *search view* is considered a view type by the main Odoo framework, but
1822 handled separately by the web client (as it's a more permanent fixture and
1823 can interact with other views, which regular views don't do).
1824
1825 A view is responsible for loading its own description XML (using
1826 :py:class:`~openerp.models.Model.fields_view_get`) and any other data source
1827 it needs. To that purpose, views are provided with an optional view
1828 identifier set as the :attr:`~openerp.web.View.view_id` attribute.
1829
1830 Views are also provided with a :class:`~openerp.web.DataSet` instance which
1831 holds most necessary model information (the model name and possibly various
1832 record ids).
1833
1834 Views may also want to handle search queries by overriding
1835 :func:`~openerp.web.View.do_search`, and updating their
1836 :class:`~openerp.web.DataSet` as necessary.
1837
1838 The Form View Fields
1839 --------------------
1840
1841 A common Odoo web need is the extension of the form view to add new ways of
1842 displaying form fields.
1843
1844 All built-in fields have a default display implementation, creating a new
1845 form widget may be necessary to correctly interact with a new field type
1846 (e.g. a :term:`GIS` field) or to provide new representations and ways to
1847 interact with existing field types (e.g. validate
1848 :py:class:`~openerp.fields.Char` fields which should contain email addresses
1849 and display them as email links).
1850
1851 To explicitly specify which form widget should be used to display a field,
1852 simply use the ``widget`` attribute in the view's XML description:
1853
1854 .. code-block:: xml
1855
1856     <field name="contact_mail" widget="email"/>
1857
1858 .. note::
1859
1860     * the same widget is used in both "view" (read-only) and "edition" modes
1861       of a form view, it's not possible to use a widget in one and an other
1862       widget in the other
1863     * and a given field (name) can not be used multiple times in the same form
1864     * a widget may ignore the current mode of the form view and remain the
1865       same in both view and edition
1866
1867 .. todo:: most of this should probably move to an advanced form view guide
1868
1869 Fields are instantiated by the form view after it has read its XML description
1870 and constructed the corresponding HTML representing that description. After
1871 that, the form view will communicate with the field objects using some
1872 methods. Theses methods are defined by the ``FieldInterface``
1873 interface. Almost all fields inherit the ``AbstractField`` abstract
1874 class. That class defines some default mechanisms that need to be implemented
1875 by most fields.
1876
1877 Here are some of the responsibilities of a field class:
1878
1879 * The field class must display and allow the user to edit the value of the field.
1880 * It must correctly implement the 3 field attributes available in all fields
1881   of Odoo. The ``AbstractField`` class already implements an algorithm that
1882   dynamically calculates the value of these attributes (they can change at any
1883   moment because their value change according to the value of other
1884   fields). Their values are stored in *Widget Properties* (the widget
1885   properties were explained earlier in this guide). It is the responsibility
1886   of each field class to check these widget properties and dynamically adapt
1887   depending of their values. Here is a description of each of these
1888   attributes:
1889
1890   * ``required``: The field must have a value before saving. If ``required``
1891     is ``true`` and the field doesn't have a value, the method
1892     ``is_valid()`` of the field must return ``false``.
1893   * ``invisible``: When this is ``true``, the field must be invisible. The
1894     ``AbstractField`` class already has a basic implementation of this
1895     behavior that fits most fields.
1896   * ``readonly``: When ``true``, the field must not be editable by the
1897     user. Most fields in Odoo have a completely different behavior depending
1898     on the value of ``readonly``. As example, the ``FieldChar`` displays an
1899     HTML ``<input>`` when it is editable and simply displays the text when
1900     it is read-only. This also means it has much more code it would need to
1901     implement only one behavior, but this is necessary to ensure a good user
1902     experience.
1903
1904 * Fields have two methods, ``set_value()`` and ``get_value()``, which are
1905   called by the form view to give it the value to display and get back the new
1906   value entered by the user. These methods must be able to handle the value as
1907   given by the Odoo server when a ``read()`` is performed on a model and give
1908   back a valid value for a ``write()``.  Remember that the JavaScript/Python
1909   data types used to represent the values given by ``read()`` and given to
1910   ``write()`` is not necessarily the same in Odoo. As example, when you read a
1911   many2one, it is always a tuple whose first value is the id of the pointed
1912   record and the second one is the name get (ie: ``(15, "Agrolait")``). But
1913   when you write a many2one it must be a single integer, not a tuple
1914   anymore. ``AbstractField`` has a default implementation of these methods
1915   that works well for simple data type and set a widget property named
1916   ``value``.
1917
1918 Please note that, to better understand how to implement fields, you are
1919 strongly encouraged to look at the definition of the ``FieldInterface``
1920 interface and the ``AbstractField`` class directly in the code of the Odoo web
1921 client.
1922
1923 Creating a New Type of Field
1924 ''''''''''''''''''''''''''''
1925
1926 In this part we will explain how to create a new type of field. The example
1927 here will be to re-implement the ``FieldChar`` class and explain progressively
1928 each part.
1929
1930 Simple Read-Only Field
1931 """"""""""""""""""""""
1932
1933 Here is a first implementation that will only be able to display a text. The
1934 user will not be able to modify the content of the field.
1935
1936 .. code-block:: javascript
1937
1938     local.FieldChar2 = instance.web.form.AbstractField.extend({
1939         init: function() {
1940             this._super.apply(this, arguments);
1941             this.set("value", "");
1942         },
1943         render_value: function() {
1944             this.$el.text(this.get("value"));
1945         },
1946     });
1947
1948     instance.web.form.widgets.add('char2', 'instance.oepetstore.FieldChar2');
1949
1950 In this example, we declare a class named ``FieldChar2`` inheriting from
1951 ``AbstractField``. We also register this class in the registry
1952 ``instance.web.form.widgets`` under the key ``char2``. That will allow us to
1953 use this new field in any form view by specifying ``widget="char2"`` in the
1954 ``<field/>`` tag in the XML declaration of the view.
1955
1956 In this example, we define a single method: ``render_value()``. All it does is
1957 display the widget property ``value``.  Those are two tools defined by the
1958 ``AbstractField`` class. As explained before, the form view will call the
1959 method ``set_value()`` of the field to set the value to display. This method
1960 already has a default implementation in ``AbstractField`` which simply sets
1961 the widget property ``value``. ``AbstractField`` also watch the
1962 ``change:value`` event on itself and calls the ``render_value()`` when it
1963 occurs. So, ``render_value()`` is a convenience method to implement in child
1964 classes to perform some operation each time the value of the field changes.
1965
1966 In the ``init()`` method, we also define the default value of the field if
1967 none is specified by the form view (here we assume the default value of a
1968 ``char`` field should be an empty string).
1969
1970 Read-Write Field
1971 """"""""""""""""
1972
1973 Fields that only display their content and don't give the possibility to the
1974 user to modify it can be useful, but most fields in Odoo allow edition
1975 too. This makes the field classes more complicated, mostly because fields are
1976 supposed to handle both and editable and non-editable mode, those modes are
1977 often completely different (for design and usability purpose) and the fields
1978 must be able to switch from one mode to another at any moment.
1979
1980 To know in which mode the current field should be, the ``AbstractField`` class
1981 sets a widget property named ``effective_readonly``. The field should watch
1982 the changes in that widget property and display the correct mode
1983 accordingly. Example::
1984
1985     local.FieldChar2 = instance.web.form.AbstractField.extend({
1986         init: function() {
1987             this._super.apply(this, arguments);
1988             this.set("value", "");
1989         },
1990         start: function() {
1991             this.on("change:effective_readonly", this, function() {
1992                 this.display_field();
1993                 this.render_value();
1994             });
1995             this.display_field();
1996             return this._super();
1997         },
1998         display_field: function() {
1999             var self = this;
2000             this.$el.html(QWeb.render("FieldChar2", {widget: this}));
2001             if (! this.get("effective_readonly")) {
2002                 this.$("input").change(function() {
2003                     self.internal_set_value(self.$("input").val());
2004                 });
2005             }
2006         },
2007         render_value: function() {
2008             if (this.get("effective_readonly")) {
2009                 this.$el.text(this.get("value"));
2010             } else {
2011                 this.$("input").val(this.get("value"));
2012             }
2013         },
2014     });
2015
2016     instance.web.form.widgets.add('char2', 'instance.oepetstore.FieldChar2');
2017
2018 .. code-block:: xml
2019
2020     <t t-name="FieldChar2">
2021         <div class="oe_field_char2">
2022             <t t-if="! widget.get('effective_readonly')">
2023                 <input type="text"></input>
2024             </t>
2025         </div>
2026     </t>
2027
2028 In the ``start()`` method (which is called right after a widget has been
2029 appended to the DOM), we bind on the event ``change:effective_readonly``. That
2030 will allow use to redisplay the field each time the widget property
2031 ``effective_readonly`` changes. This event handler will call
2032 ``display_field()``, which is also called directly in ``start()``. This
2033 ``display_field()`` was created specifically for this field, it's not a method
2034 defined in ``AbstractField`` or any other class. This is the method we will
2035 use to display the content of the field depending we are in read-only mode or
2036 not.
2037
2038 From now on the conception of this field is quite typical, except there is a
2039 lot of verifications to know the state of the ``effective_readonly`` property:
2040
2041 * In the QWeb template used to display the content of the widget, it displays
2042   an ``<input type="text" />`` if we are in read-write mode and nothing in
2043   particular in read-only mode.
2044 * In the ``display_field()`` method, we have to bind on the ``change`` event
2045   of the ``<input type="text" />`` to know when the user has changed the
2046   value. When it happens, we call the ``internal_set_value()`` method with the
2047   new value of the field. This is a convenience method provided by the
2048   ``AbstractField`` class. That method will set a new value in the ``value``
2049   property but will not trigger a call to ``render_value()`` (which is not
2050   necessary since the ``<input type="text" />`` already contains the correct
2051   value).
2052 * In ``render_value()``, we use a completely different code to display the
2053   value of the field depending if we are in read-only or in read-write mode.
2054
2055 .. exercise:: Create a Color Field
2056
2057     Create a ``FieldColor`` class. The value of this field should be a string
2058     containing a color code like those used in CSS (example: ``#FF0000`` for
2059     red). In read-only mode, this color field should display a little block
2060     whose color corresponds to the value of the field. In read-write mode, you
2061     should display an ``<input type="color" />``. That type of ``<input />``
2062     is an HTML5 component that doesn't work in all browsers but works well in
2063     Google Chrome. So it's OK to use as an exercise.
2064
2065     You can use that widget in the form view of the ``message_of_the_day``
2066     model for its field named ``color``. As a bonus, you can change the
2067     ``MessageOfTheDay`` widget created in the previous part of this guide to
2068     display the message of the day with the background color indicated in the
2069     ``color`` field.
2070
2071     .. only:: solutions
2072
2073         .. code-block:: javascript
2074
2075             local.FieldColor = instance.web.form.AbstractField.extend({
2076                 events: {
2077                     'change input': function (e) {
2078                         if (!this.get('effective_readonly')) {
2079                             this.internal_set_value($(e.currentTarget).val());
2080                         }
2081                     }
2082                 },
2083                 init: function() {
2084                     this._super.apply(this, arguments);
2085                     this.set("value", "");
2086                 },
2087                 start: function() {
2088                     this.on("change:effective_readonly", this, function() {
2089                         this.display_field();
2090                         this.render_value();
2091                     });
2092                     this.display_field();
2093                     return this._super();
2094                 },
2095                 display_field: function() {
2096                     this.$el.html(QWeb.render("FieldColor", {widget: this}));
2097                 },
2098                 render_value: function() {
2099                     if (this.get("effective_readonly")) {
2100                         this.$(".oe_field_color_content").css("background-color", this.get("value") || "#FFFFFF");
2101                     } else {
2102                         this.$("input").val(this.get("value") || "#FFFFFF");
2103                     }
2104                 },
2105             });
2106             instance.web.form.widgets.add('color', 'instance.oepetstore.FieldColor');
2107
2108         .. code-block:: xml
2109
2110             <t t-name="FieldColor">
2111                 <div class="oe_field_color">
2112                     <t t-if="widget.get('effective_readonly')">
2113                         <div class="oe_field_color_content" />
2114                     </t>
2115                     <t t-if="! widget.get('effective_readonly')">
2116                         <input type="color"></input>
2117                     </t>
2118                 </div>
2119             </t>
2120
2121         .. code-block:: css
2122
2123             .oe_field_color_content {
2124                 height: 20px;
2125                 width: 50px;
2126                 border: 1px solid black;
2127             }
2128
2129 The Form View Custom Widgets
2130 ----------------------------
2131
2132 Form fields are used to edit a single field, and are intrinsically linked to
2133 a field. Because this may be limiting, it is also possible to create
2134 *form widgets* which are not so restricted and have less ties to a specific
2135 lifecycle.
2136
2137 Custom form widgets can be added to a form view through the ``widget`` tag:
2138
2139 .. code-block:: xml
2140
2141     <widget type="xxx" />
2142
2143 This type of widget will simply be created by the form view during the
2144 creation of the HTML according to the XML definition. They have properties in
2145 common with the fields (like the ``effective_readonly`` property) but they are
2146 not assigned a precise field. And so they don't have methods like
2147 ``get_value()`` and ``set_value()``. They must inherit from the ``FormWidget``
2148 abstract class.
2149
2150 Form widgets can interact with form fields by listening for their changes and
2151 fetching or altering their values. They can access form fields through
2152 their :attr:`~openerp.web.form.FormWidget.field_manager` attribute::
2153
2154     local.WidgetMultiplication = instance.web.form.FormWidget.extend({
2155         start: function() {
2156             this._super();
2157             this.field_manager.on("field_changed:integer_a", this, this.display_result);
2158             this.field_manager.on("field_changed:integer_b", this, this.display_result);
2159             this.display_result();
2160         },
2161         display_result: function() {
2162             var result = this.field_manager.get_field_value("integer_a") *
2163                          this.field_manager.get_field_value("integer_b");
2164             this.$el.text("a*b = " + result);
2165         }
2166     });
2167
2168     instance.web.form.custom_widgets.add('multiplication', 'instance.oepetstore.WidgetMultiplication');
2169
2170 :attr:`~openerp.web.form.FormWidget` is generally the
2171 :class:`~openerp.web.form.FormView` itself, but features used from it should
2172 be limited to those defined by :class:`~openerp.web.form.FieldManagerMixin`,
2173 the most useful being:
2174
2175 * :func:`~openerp.web.form.FieldManagerMixin.get_field_value(field_name)`
2176   which returns the value of a field.
2177 * :func:`~openerp.web.form.FieldManagerMixin.set_values(values)` sets multiple
2178   field values, takes a mapping of ``{field_name: value_to_set}``
2179 * An event :samp:`field_changed:{field_name}` is triggered any time the value
2180   of the field called ``field_name`` is changed
2181
2182 .. exercise:: Show Coordinates on Google Map
2183
2184     Add two fields to ``product.product`` storing a latitude and a longitude,
2185     then create a new form widget to display the latitude and longitude of
2186     a product's origin on a map
2187
2188     To display the map, use Google Map's embedding:
2189
2190     .. code-block:: html
2191
2192         <iframe width="400" height="300" src="https://maps.google.com/?ie=UTF8&amp;ll=XXX,YYY&amp;output=embed">
2193         </iframe>
2194
2195     where ``XXX`` should be replaced by the latitude and ``YYY`` by the
2196     longitude.
2197
2198     Display the two position fields and a map widget using them in a new
2199     notebook page of the product's form view.
2200
2201     .. only:: solutions
2202
2203         .. code-block:: javascript
2204
2205             local.WidgetCoordinates = instance.web.form.FormWidget.extend({
2206                 start: function() {
2207                     this._super();
2208                     this.field_manager.on("field_changed:provider_latitude", this, this.display_map);
2209                     this.field_manager.on("field_changed:provider_longitude", this, this.display_map);
2210                     this.display_map();
2211                 },
2212                 display_map: function() {
2213                     this.$el.html(QWeb.render("WidgetCoordinates", {
2214                         "latitude": this.field_manager.get_field_value("provider_latitude") || 0,
2215                         "longitude": this.field_manager.get_field_value("provider_longitude") || 0,
2216                     }));
2217                 }
2218             });
2219
2220             instance.web.form.custom_widgets.add('coordinates', 'local.WidgetCoordinates');
2221
2222         .. code-block:: xml
2223
2224             <t t-name="WidgetCoordinates">
2225                 <iframe width="400" height="300"
2226                     t-attf-src="https://maps.google.com/?ie=UTF8&amp;ll={{latitude}},{{longitude}}&amp;output=embed">
2227                 </iframe>
2228             </t>
2229
2230 .. exercise:: Get the Current Coordinate
2231
2232     Add a button resetting the product's coordinates to the location of the
2233     user, you can get these coordinates using the
2234     `javascript geolocation API`_.
2235
2236     Now we would like to display an additional button to automatically set the
2237     coordinates to the location of the current user.
2238
2239     To get the coordinates of the user, an easy way is to use the geolocation
2240     JavaScript API.  `See the online documentation to know how to use it`_.
2241
2242     .. _See the online documentation to know how to use it: http://www.w3schools.com/html/html5_geolocation.asp
2243
2244     Please also note that it wouldn't be very logical to allow the user to
2245     click on that button when the form view is in read-only mode. So, this
2246     custom widget should handle correctly the ``effective_readonly`` property
2247     just like any field. One way to do this would be to make the button
2248     disappear when ``effective_readonly`` is true.
2249
2250     .. only:: solutions
2251
2252         .. code-block:: javascript
2253
2254             local.WidgetCoordinates = instance.web.form.FormWidget.extend({
2255                 events: {
2256                     'click button': function () {
2257                         navigator.geolocation.getCurrentPosition(
2258                             this.proxy('received_position'));
2259                     }
2260                 },
2261                 start: function() {
2262                     var sup = this._super();
2263                     this.field_manager.on("field_changed:provider_latitude", this, this.display_map);
2264                     this.field_manager.on("field_changed:provider_longitude", this, this.display_map);
2265                     this.on("change:effective_readonly", this, this.display_map);
2266                     this.display_map();
2267                     return sup;
2268                 },
2269                 display_map: function() {
2270                     this.$el.html(QWeb.render("WidgetCoordinates", {
2271                         "latitude": this.field_manager.get_field_value("provider_latitude") || 0,
2272                         "longitude": this.field_manager.get_field_value("provider_longitude") || 0,
2273                     }));
2274                     this.$("button").toggle(! this.get("effective_readonly"));
2275                 },
2276                 received_position: function(obj) {
2277                     this.field_manager.set_values({
2278                         "provider_latitude": obj.coords.latitude,
2279                         "provider_longitude": obj.coords.longitude,
2280                     });
2281                 },
2282             });
2283
2284             instance.web.form.custom_widgets.add('coordinates', 'local.WidgetCoordinates');
2285
2286         .. code-block:: xml
2287
2288             <t t-name="WidgetCoordinates">
2289                 <iframe width="400" height="300"
2290                     t-att-src="https://maps.google.com/?ie=UTF8&amp;ll={{latitude}},{{longitude}}&amp;output=embed">
2291                 </iframe>
2292                 <button>Get My Current Coordinate</button>
2293             </t>
2294
2295 .. [#classes] as a separate concept from instances. In many languages classes
2296               are full-fledged objects and themselves instance (of
2297               metaclasses) but there remains two fairly separate hierarchies
2298               between classes and instances
2299 .. [#dombugs] as well as papering over cross-browser differences, although
2300               this has become less necessary over time
2301
2302 .. _jQuery: http://jquery.org
2303 .. _Underscore.js: http://underscorejs.org
2304 .. _git: http://git-scm.com
2305 .. _CSS: http://www.w3.org/Style/CSS/Overview.en.html
2306 .. _Simple JavaScript Inheritance:
2307     http://ejohn.org/blog/simple-javascript-inheritance/
2308 .. _W3C DOM: http://www.w3.org/TR/DOM-Level-3-Core/
2309 .. _Qt: http://qt-project.org
2310 .. _Cocoa: https://developer.apple.com/technologies/mac/cocoa.html
2311 .. _GTK: http://www.gtk.org
2312 .. _template engine: http://en.wikipedia.org/wiki/Web_template_system
2313 .. _cross-site scripting: http://en.wikipedia.org/wiki/Cross-site_scripting
2314 .. _jQuery event object: http://api.jquery.com/category/events/event-object/
2315 .. _$.ajax: http://api.jquery.com/jquery.ajax/
2316 .. _base64: http://en.wikipedia.org/wiki/Base64
2317 .. _javascript geolocation API:
2318     http://diveintohtml5.info/geolocation.html
2319 .. _PostgreSQL: http://en.wikipedia.org/wiki/PostgreSQL
2320 .. _positional arguments:
2321 .. _keyword arguments:
2322     https://docs.python.org/2/glossary.html#term-argument