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