[FIX] lot of potential bugs, mainly due to quickly clicking on menu items
[odoo/odoo.git] / addons / web / doc / widget.rst
1 Widget
2 ======
3
4 .. js:class:: openerp.web.Widget
5
6 This is the base class for all visual components. It corresponds to an MVC
7 view. It provides a number of services to handle a section of a page:
8
9 * Rendering with QWeb
10
11 * Parenting-child relations
12
13 * Life-cycle management (including facilitating children destruction when a
14   parent object is removed)
15
16 * DOM insertion, via jQuery-powered insertion methods. Insertion targets can
17   be anything the corresponding jQuery method accepts (generally selectors,
18   DOM nodes and jQuery objects):
19
20   :js:func:`~openerp.web.Widget.appendTo`
21     Renders the widget and inserts it as the last child of the target, uses
22     `.appendTo()`_
23
24   :js:func:`~openerp.web.Widget.prependTo`
25     Renders the widget and inserts it as the first child of the target, uses
26     `.prependTo()`_
27
28   :js:func:`~openerp.web.Widget.insertAfter`
29     Renders the widget and inserts it as the preceding sibling of the target,
30     uses `.insertAfter()`_
31
32   :js:func:`~openerp.web.Widget.insertBefore`
33     Renders the widget and inserts it as the following sibling of the target,
34     uses `.insertBefore()`_
35
36 * Backbone-compatible shortcuts
37
38 .. _widget-dom_root:
39
40 DOM Root
41 --------
42
43 A :js:class:`~openerp.web.Widget` is responsible for a section of the
44 page materialized by the DOM root of the widget. The DOM root is
45 available via the :js:attr:`~openerp.web.Widget.el` and
46 :js:attr:`~openerp.web.Widget.$el` attributes, which are
47 respectively the raw DOM Element and the jQuery wrapper around the DOM
48 element.
49
50 There are two main ways to define and generate this DOM root:
51
52 .. js:attribute:: openerp.web.Widget.template
53
54     Should be set to the name of a QWeb template (a
55     :js:class:`String`). If set, the template will be rendered after
56     the widget has been initialized but before it has been
57     started. The root element generated by the template will be set as
58     the DOM root of the widget.
59
60 .. js:attribute:: openerp.web.Widget.tagName
61
62     Used if the widget has no template defined. Defaults to ``div``,
63     will be used as the tag name to create the DOM element to set as
64     the widget's DOM root. It is possible to further customize this
65     generated DOM root with the following attributes:
66
67     .. js:attribute:: openerp.web.Widget.id
68
69         Used to generate an ``id`` attribute on the generated DOM
70         root.
71
72     .. js:attribute:: openerp.web.Widget.className
73
74         Used to generate a ``class`` attribute on the generated DOM root.
75
76     .. js:attribute:: openerp.web.Widget.attributes
77
78         Mapping (object literal) of attribute names to attribute
79         values. Each of these k:v pairs will be set as a DOM attribute
80         on the generated DOM root.
81
82     None of these is used in case a template is specified on the widget.
83
84 The DOM root can also be defined programmatically by overridding
85
86 .. js:function:: openerp.web.Widget.renderElement
87
88     Renders the widget's DOM root and sets it. The default
89     implementation will render a set template or generate an element
90     as described above, and will call
91     :js:func:`~openerp.web.Widget.setElement` on the result.
92
93     Any override to :js:func:`~openerp.web.Widget.renderElement` which
94     does not call its ``_super`` **must** call
95     :js:func:`~openerp.web.Widget.setElement` with whatever it
96     generated or the widget's behavior is undefined.r
97
98     .. note::
99
100         The default :js:func:`~openerp.web.Widget.renderElement` can
101         be called repeatedly, it will *replace* the previous DOM root
102         (using ``replaceWith``). However, this requires that the
103         widget correctly sets and unsets its events (and children
104         widgets). Generally,
105         :js:func:`~openerp.web.Widget.renderElement` should not be
106         called repeatedly unless the widget advertizes this feature.
107
108 Accessing DOM content
109 ~~~~~~~~~~~~~~~~~~~~~
110
111 Because a widget is only responsible for the content below its DOM
112 root, there is a shortcut for selecting sub-sections of a widget's
113 DOM:
114
115 .. js:function:: openerp.web.Widget.$(selector)
116
117     Applies the CSS selector specified as parameter to the widget's
118     DOM root.
119
120     .. code-block:: javascript
121
122         this.$(selector);
123
124     is functionally identical to:
125
126     .. code-block:: javascript
127
128         this.$el.find(selector);
129
130     :param String selector: CSS selector
131     :returns: jQuery object
132
133     .. note:: this helper method is compatible with
134               ``Backbone.View.$``
135
136 Resetting the DOM root
137 ~~~~~~~~~~~~~~~~~~~~~~
138
139 .. js:function:: openerp.web.Widget.setElement(element)
140
141     Re-sets the widget's DOM root to the provided element, also
142     handles re-setting the various aliases of the DOM root as well as
143     unsetting and re-setting delegated events.
144
145     :param Element element: a DOM element or jQuery object to set as
146                             the widget's DOM root
147
148     .. note:: should be mostly compatible with `Backbone's
149               setElement`_
150
151 DOM events handling
152 -------------------
153
154 A widget will generally need to respond to user action within its
155 section of the page. This entails binding events to DOM elements.
156
157 To this end, :js:class:`~openerp.web.Widget` provides an shortcut:
158
159 .. js:attribute:: openerp.web.Widget.events
160
161     Events are a mapping of ``event selector`` (an event name and a
162     CSS selector separated by a space) to a callback. The callback can
163     be either a method name in the widget or a function. In either
164     case, the ``this`` will be set to the widget:
165
166     .. code-block:: javascript
167
168         events: {
169             'click p.oe_some_class a': 'some_method',
170             'change input': function (e) {
171                 e.stopPropagation();
172             }
173         },
174
175     The selector is used for jQuery's `event delegation`_, the
176     callback will only be triggered for descendants of the DOM root
177     matching the selector [0]_. If the selector is left out (only an
178     event name is specified), the event will be set directly on the
179     widget's DOM root.
180
181 .. js:function:: openerp.web.Widget.delegateEvents
182
183     This method is in charge of binding
184     :js:attr:`~openerp.web.Widget.events` to the DOM. It is
185     automatically called after setting the widget's DOM root.
186
187     It can be overridden to set up more complex events than the
188     :js:attr:`~openerp.web.Widget.events` map allows, but the parent
189     should always be called (or :js:attr:`~openerp.web.Widget.events`
190     won't be handled correctly).
191
192 .. js:function:: openerp.web.Widget.undelegateEvents
193
194     This method is in charge of unbinding
195     :js:attr:`~openerp.web.Widget.events` from the DOM root when the
196     widget is destroyed or the DOM root is reset, in order to avoid
197     leaving "phantom" events.
198
199     It should be overridden to un-set any event set in an override of
200     :js:func:`~openerp.web.Widget.delegateEvents`.
201
202 .. note:: this behavior should be compatible with `Backbone's
203           delegateEvents`_, apart from not accepting any argument.
204
205 Subclassing Widget
206 ------------------
207
208 :js:class:`~openerp.base.Widget` is subclassed in the standard manner (via the
209 :js:func:`~openerp.base.Class.extend` method), and provides a number of
210 abstract properties and concrete methods (which you may or may not want to
211 override). Creating a subclass looks like this:
212
213 .. code-block:: javascript
214
215     var MyWidget = openerp.base.Widget.extend({
216         // QWeb template to use when rendering the object
217         template: "MyQWebTemplate",
218
219         init: function(parent) {
220             this._super(parent);
221             // insert code to execute before rendering, for object
222             // initialization
223         },
224         start: function() {
225             this._super();
226             // post-rendering initialization code, at this point
227             // ``this.$element`` has been initialized
228             this.$element.find(".my_button").click(/* an example of event binding * /);
229
230             // if ``start`` is asynchronous, return a promise object so callers
231             // know when the object is done initializing
232             return this.rpc(/* … */)
233         }
234     });
235
236 The new class can then be used in the following manner:
237
238 .. code-block:: javascript
239
240     // Create the instance
241     var my_widget = new MyWidget(this);
242     // Render and insert into DOM
243     my_widget.appendTo(".some-div");
244
245 After these two lines have executed (and any promise returned by ``appendTo``
246 has been resolved if needed), the widget is ready to be used.
247
248 .. note:: the insertion methods will start the widget themselves, and will
249           return the result of :js:func:`~openerp.base.Widget.start()`.
250
251           If for some reason you do not want to call these methods, you will
252           have to first call :js:func:`~openerp.base.Widget.render()` on the
253           widget, then insert it into your DOM and start it.
254
255 If the widget is not needed anymore (because it's transient), simply terminate
256 it:
257
258 .. code-block:: javascript
259
260     my_widget.destroy();
261
262 will unbind all DOM events, remove the widget's content from the DOM and
263 destroy all widget data.
264
265 .. [0] not all DOM events are compatible with events delegation
266
267 .. _.appendTo():
268     http://api.jquery.com/appendTo/
269
270 .. _.prependTo():
271     http://api.jquery.com/prependTo/
272
273 .. _.insertAfter():
274     http://api.jquery.com/insertAfter/
275
276 .. _.insertBefore():
277     http://api.jquery.com/insertBefore/
278
279 .. _event delegation:
280     http://api.jquery.com/delegate/
281
282 .. _Backbone's setElement:
283     http://backbonejs.org/#View-setElement
284
285 .. _Backbone's delegateEvents:
286     http://backbonejs.org/#View-delegateEvents
287