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